mirror of
https://github.com/frosch95/K8sFileBrowser.git
synced 2026-04-11 21:08:22 +02:00
Compare commits
4 Commits
v0.0.5-alp
...
v0.0.7-alp
| Author | SHA1 | Date | |
|---|---|---|---|
| b26cc0e984 | |||
| ba39661926 | |||
| 59e9131fed | |||
| d544f75c99 |
@@ -9,7 +9,7 @@
|
|||||||
<Configurations>Debug;Release</Configurations>
|
<Configurations>Debug;Release</Configurations>
|
||||||
<Platforms>AnyCPU</Platforms>
|
<Platforms>AnyCPU</Platforms>
|
||||||
<ApplicationIcon>Assets/app.ico</ApplicationIcon>
|
<ApplicationIcon>Assets/app.ico</ApplicationIcon>
|
||||||
<Version>0.0.3</Version>
|
<Version>0.0.7</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||||
<DefineConstants>TRACE</DefineConstants>
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
|||||||
23
K8sFileBrowser/Models/Container.cs
Normal file
23
K8sFileBrowser/Models/Container.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
namespace K8sFileBrowser.Models;
|
||||||
|
|
||||||
|
public class Container
|
||||||
|
{
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object? obj)
|
||||||
|
{
|
||||||
|
if (obj is Container container)
|
||||||
|
{
|
||||||
|
return Name == container.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode() => Name.GetHashCode();
|
||||||
|
}
|
||||||
@@ -6,10 +6,18 @@ public class FileInformation
|
|||||||
{
|
{
|
||||||
public string Parent { get; set; } = string.Empty;
|
public string Parent { get; set; } = string.Empty;
|
||||||
public FileType Type { get; set; } = FileType.File;
|
public FileType Type { get; set; } = FileType.File;
|
||||||
public string DisplayName => Parent.Length < 2 ? Name[Parent.Length..] : Name[( Parent.Length + 1)..];
|
public string DisplayName
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (".." == Name) return "..";
|
||||||
|
return Parent.Length < 2 ? Name[Parent.Length..] : Name[(Parent.Length + 1)..];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public string Name { get; set; } = string.Empty;
|
public string Name { get; set; } = string.Empty;
|
||||||
public string Size { get; set; } = string.Empty;
|
public string Size { get; set; } = string.Empty;
|
||||||
public DateTimeOffset Date { get; set; } = DateTimeOffset.MinValue;
|
public DateTimeOffset? Date { get; set; }
|
||||||
|
|
||||||
public bool IsFile => Type == FileType.File;
|
public bool IsFile => Type == FileType.File;
|
||||||
public bool IsDirectory => Type == FileType.Directory;
|
public bool IsDirectory => Type == FileType.Directory;
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ public interface IKubernetesService
|
|||||||
Task<IEnumerable<Namespace>> GetNamespacesAsync();
|
Task<IEnumerable<Namespace>> GetNamespacesAsync();
|
||||||
Task<IEnumerable<Pod>> GetPodsAsync(string namespaceName, CancellationToken cancellationToken = default);
|
Task<IEnumerable<Pod>> GetPodsAsync(string namespaceName, CancellationToken cancellationToken = default);
|
||||||
IList<FileInformation> GetFiles(string namespaceName, string podName, string containerName, string path);
|
IList<FileInformation> GetFiles(string namespaceName, string podName, string containerName, string path);
|
||||||
Task DownloadFile(Namespace? selectedNamespace, Pod? selectedPod, FileInformation selectedFile,
|
Task DownloadFile(Namespace? selectedNamespace, Pod? selectedPod, Container? selectedContainer, FileInformation selectedFile,
|
||||||
string? saveFileName, CancellationToken cancellationToken = default);
|
string? saveFileName, CancellationToken cancellationToken = default);
|
||||||
Task DownloadLog(Namespace? selectedNamespace, Pod? selectedPod,
|
Task DownloadLog(Namespace? selectedNamespace, Pod? selectedPod, Container? selectedContainer,
|
||||||
string? saveFileName, CancellationToken cancellationToken = default);
|
string? saveFileName, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -98,7 +98,7 @@ public class KubernetesService : IKubernetesService
|
|||||||
_kubernetesClient = kubernetesClient;
|
_kubernetesClient = kubernetesClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DownloadFile(Namespace? selectedNamespace, Pod? selectedPod, FileInformation selectedFile,
|
public async Task DownloadFile(Namespace? selectedNamespace, Pod? selectedPod, Container? selectedContainer, FileInformation selectedFile,
|
||||||
string? saveFileName, CancellationToken cancellationToken = default)
|
string? saveFileName, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
Log.Information("{SelectedNamespace} - {SelectedPod} - {@SelectedFile} - {SaveFileName}",
|
Log.Information("{SelectedNamespace} - {SelectedPod} - {@SelectedFile} - {SaveFileName}",
|
||||||
@@ -139,14 +139,14 @@ public class KubernetesService : IKubernetesService
|
|||||||
await _kubernetesClient.NamespacedPodExecAsync(
|
await _kubernetesClient.NamespacedPodExecAsync(
|
||||||
selectedPod?.Name,
|
selectedPod?.Name,
|
||||||
selectedNamespace?.Name,
|
selectedNamespace?.Name,
|
||||||
selectedPod?.Containers.First(),
|
selectedContainer?.Name,
|
||||||
new[] { "sh", "-c", $"tar cf - {selectedFile.Name}" },
|
new[] { "sh", "-c", $"tar cf - {selectedFile.Name}" },
|
||||||
false,
|
false,
|
||||||
handler,
|
handler,
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DownloadLog(Namespace? selectedNamespace, Pod? selectedPod,
|
public async Task DownloadLog(Namespace? selectedNamespace, Pod? selectedPod, Container? selectedContainer,
|
||||||
string? saveFileName, CancellationToken cancellationToken = default)
|
string? saveFileName, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
Log.Information("{SelectedNamespace} - {SelectedPod} - {SaveFileName}",
|
Log.Information("{SelectedNamespace} - {SelectedPod} - {SaveFileName}",
|
||||||
@@ -155,7 +155,7 @@ public class KubernetesService : IKubernetesService
|
|||||||
var response = await _kubernetesClient.CoreV1.ReadNamespacedPodLogWithHttpMessagesAsync(
|
var response = await _kubernetesClient.CoreV1.ReadNamespacedPodLogWithHttpMessagesAsync(
|
||||||
selectedPod?.Name,
|
selectedPod?.Name,
|
||||||
selectedNamespace?.Name,
|
selectedNamespace?.Name,
|
||||||
container: selectedPod?.Containers.First(),
|
container: selectedContainer?.Name,
|
||||||
follow: false , cancellationToken: cancellationToken)
|
follow: false , cancellationToken: cancellationToken)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ namespace K8sFileBrowser.ViewModels;
|
|||||||
|
|
||||||
public class MainWindowViewModel : ViewModelBase
|
public class MainWindowViewModel : ViewModelBase
|
||||||
{
|
{
|
||||||
private ObservableAsPropertyHelper<IEnumerable<ClusterContext>> _clusterContexts;
|
private ObservableAsPropertyHelper<IEnumerable<ClusterContext>> _clusterContexts = null!;
|
||||||
public IEnumerable<ClusterContext> ClusterContexts => _clusterContexts.Value;
|
public IEnumerable<ClusterContext> ClusterContexts => _clusterContexts.Value;
|
||||||
|
|
||||||
private ClusterContext? _selectedClusterContext;
|
private ClusterContext? _selectedClusterContext;
|
||||||
@@ -24,7 +24,7 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
set => this.RaiseAndSetIfChanged(ref _selectedClusterContext, value);
|
set => this.RaiseAndSetIfChanged(ref _selectedClusterContext, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<Namespace> _namespaces;
|
private IEnumerable<Namespace> _namespaces = null!;
|
||||||
public IEnumerable<Namespace> Namespaces
|
public IEnumerable<Namespace> Namespaces
|
||||||
{
|
{
|
||||||
get => _namespaces;
|
get => _namespaces;
|
||||||
@@ -39,7 +39,7 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
set => this.RaiseAndSetIfChanged(ref _selectedNamespace, value);
|
set => this.RaiseAndSetIfChanged(ref _selectedNamespace, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ObservableAsPropertyHelper<IEnumerable<Pod>> _pods;
|
private ObservableAsPropertyHelper<IEnumerable<Pod>> _pods = null!;
|
||||||
public IEnumerable<Pod> Pods => _pods.Value;
|
public IEnumerable<Pod> Pods => _pods.Value;
|
||||||
|
|
||||||
private Pod? _selectedPod;
|
private Pod? _selectedPod;
|
||||||
@@ -50,7 +50,22 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
set => this.RaiseAndSetIfChanged(ref _selectedPod, value);
|
set => this.RaiseAndSetIfChanged(ref _selectedPod, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ObservableAsPropertyHelper<IEnumerable<FileInformation>> _fileInformation;
|
private IEnumerable<Container>? _containers;
|
||||||
|
public IEnumerable<Container>? Containers
|
||||||
|
{
|
||||||
|
get => _containers;
|
||||||
|
set => this.RaiseAndSetIfChanged(ref _containers, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Container? _selectedContainer;
|
||||||
|
|
||||||
|
public Container? SelectedContainer
|
||||||
|
{
|
||||||
|
get => _selectedContainer;
|
||||||
|
set => this.RaiseAndSetIfChanged(ref _selectedContainer, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ObservableAsPropertyHelper<IEnumerable<FileInformation>> _fileInformation = null!;
|
||||||
public IEnumerable<FileInformation> FileInformation => _fileInformation.Value;
|
public IEnumerable<FileInformation> FileInformation => _fileInformation.Value;
|
||||||
|
|
||||||
private FileInformation? _selectedFile;
|
private FileInformation? _selectedFile;
|
||||||
@@ -69,19 +84,19 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
set => this.RaiseAndSetIfChanged(ref _selectedPath, value);
|
set => this.RaiseAndSetIfChanged(ref _selectedPath, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Message _message;
|
private Message _message = null!;
|
||||||
public Message Message
|
public Message Message
|
||||||
{
|
{
|
||||||
get => _message;
|
get => _message;
|
||||||
set => this.RaiseAndSetIfChanged(ref _message, value);
|
set => this.RaiseAndSetIfChanged(ref _message, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> DownloadCommand { get; private set; }
|
public ReactiveCommand<Unit, Unit> DownloadCommand { get; private set; } = null!;
|
||||||
public ReactiveCommand<Unit, Unit> DownloadLogCommand { get; private set; }
|
public ReactiveCommand<Unit, Unit> DownloadLogCommand { get; private set; } = null!;
|
||||||
public ReactiveCommand<Unit, Unit> ParentCommand { get; private set; }
|
public ReactiveCommand<Unit, Unit> ParentCommand { get; private set; } = null!;
|
||||||
public ReactiveCommand<Unit, Unit> OpenCommand { get; private set; }
|
public ReactiveCommand<Unit, Unit> OpenCommand { get; private set; } = null!;
|
||||||
|
|
||||||
private ReactiveCommand<Namespace, IEnumerable<Pod>> GetPodsForNamespace { get; set; }
|
private ReactiveCommand<Namespace, IEnumerable<Pod>> GetPodsForNamespace { get; set; } = null!;
|
||||||
|
|
||||||
|
|
||||||
public MainWindowViewModel()
|
public MainWindowViewModel()
|
||||||
@@ -99,6 +114,7 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
// register the listeners
|
// register the listeners
|
||||||
RegisterReadNamespaces(kubernetesService);
|
RegisterReadNamespaces(kubernetesService);
|
||||||
RegisterReadPods();
|
RegisterReadPods();
|
||||||
|
RegisterReadContainers();
|
||||||
RegisterReadFiles(kubernetesService);
|
RegisterReadFiles(kubernetesService);
|
||||||
RegisterResetPath();
|
RegisterResetPath();
|
||||||
|
|
||||||
@@ -124,19 +140,37 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
// reset the path when the pod or namespace changes
|
// reset the path when the pod or namespace changes
|
||||||
this.WhenAnyValue(c => c.SelectedPod, c => c.SelectedNamespace)
|
this.WhenAnyValue(c => c.SelectedPod, c => c.SelectedNamespace)
|
||||||
.Throttle(new TimeSpan(10))
|
.Throttle(new TimeSpan(10))
|
||||||
.Subscribe(x => SelectedPath = "/");
|
.ObserveOn(RxApp.TaskpoolScheduler)
|
||||||
|
.Subscribe(_ => SelectedPath = "/");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void RegisterReadContainers()
|
||||||
|
{
|
||||||
|
// read the file information when the path changes
|
||||||
|
this
|
||||||
|
.WhenAnyValue(c => c.SelectedPod, c => c.SelectedNamespace)
|
||||||
|
.Throttle(new TimeSpan(10))
|
||||||
|
.Select(x => x.Item2 == null || x.Item1 == null
|
||||||
|
? new List<Container>()
|
||||||
|
: x.Item1.Containers.Select(c => new Container {Name = c}))
|
||||||
|
.ObserveOn(RxApp.MainThreadScheduler)
|
||||||
|
.Subscribe( x => Containers = x);
|
||||||
|
|
||||||
|
this.WhenAnyValue(x => x.Containers)
|
||||||
|
.Throttle(new TimeSpan(10))
|
||||||
|
.ObserveOn(RxApp.MainThreadScheduler)
|
||||||
|
.Subscribe(x => SelectedContainer = x?.FirstOrDefault());
|
||||||
|
}
|
||||||
|
|
||||||
private void RegisterReadFiles(IKubernetesService kubernetesService)
|
private void RegisterReadFiles(IKubernetesService kubernetesService)
|
||||||
{
|
{
|
||||||
// read the file information when the path changes
|
// read the file information when the path changes
|
||||||
_fileInformation = this
|
_fileInformation = this
|
||||||
.WhenAnyValue(c => c.SelectedPath, c => c.SelectedPod, c => c.SelectedNamespace)
|
.WhenAnyValue(c => c.SelectedPath, c => c.SelectedPod, c => c.SelectedNamespace, c => c.SelectedContainer)
|
||||||
.Throttle(new TimeSpan(10))
|
.Throttle(new TimeSpan(10))
|
||||||
.Select(x => x.Item3 == null || x.Item2 == null
|
.Select(x => x.Item3 == null || x.Item2 == null || x.Item1 == null || x.Item4 == null
|
||||||
? new List<FileInformation>()
|
? new List<FileInformation>()
|
||||||
: kubernetesService.GetFiles(x.Item3!.Name, x.Item2!.Name, x.Item2!.Containers.First(),
|
: GetFileInformation(kubernetesService, x.Item1, x.Item2, x.Item3, x.Item4))
|
||||||
x.Item1))
|
|
||||||
.ObserveOn(RxApp.MainThreadScheduler)
|
.ObserveOn(RxApp.MainThreadScheduler)
|
||||||
.ToProperty(this, x => x.FileInformation);
|
.ToProperty(this, x => x.FileInformation);
|
||||||
}
|
}
|
||||||
@@ -147,7 +181,7 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
_pods = this
|
_pods = this
|
||||||
.WhenAnyValue(c => c.SelectedNamespace)
|
.WhenAnyValue(c => c.SelectedNamespace)
|
||||||
.Throttle(new TimeSpan(10))
|
.Throttle(new TimeSpan(10))
|
||||||
.SelectMany(ns => GetPodsForNamespace.Execute(ns))
|
.SelectMany(ns => GetPodsForNamespace.Execute(ns!))
|
||||||
.ObserveOn(RxApp.MainThreadScheduler)
|
.ObserveOn(RxApp.MainThreadScheduler)
|
||||||
.ToProperty(this, x => x.Pods);
|
.ToProperty(this, x => x.Pods);
|
||||||
}
|
}
|
||||||
@@ -206,7 +240,7 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
if (saveFileName != null)
|
if (saveFileName != null)
|
||||||
{
|
{
|
||||||
ShowWorkingMessage("Downloading Log...");
|
ShowWorkingMessage("Downloading Log...");
|
||||||
await kubernetesService.DownloadLog(SelectedNamespace, SelectedPod, saveFileName);
|
await kubernetesService.DownloadLog(SelectedNamespace, SelectedPod, SelectedContainer, saveFileName);
|
||||||
HideWorkingMessage();
|
HideWorkingMessage();
|
||||||
}
|
}
|
||||||
}, RxApp.TaskpoolScheduler);
|
}, RxApp.TaskpoolScheduler);
|
||||||
@@ -232,7 +266,7 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
if (saveFileName != null)
|
if (saveFileName != null)
|
||||||
{
|
{
|
||||||
ShowWorkingMessage("Downloading File...");
|
ShowWorkingMessage("Downloading File...");
|
||||||
await kubernetesService.DownloadFile(SelectedNamespace, SelectedPod, SelectedFile, saveFileName);
|
await kubernetesService.DownloadFile(SelectedNamespace, SelectedPod, SelectedContainer, SelectedFile, saveFileName);
|
||||||
HideWorkingMessage();
|
HideWorkingMessage();
|
||||||
}
|
}
|
||||||
}, RxApp.TaskpoolScheduler);
|
}, RxApp.TaskpoolScheduler);
|
||||||
@@ -248,7 +282,13 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
.WhenAnyValue(x => x.SelectedFile, x => x.Message.IsVisible)
|
.WhenAnyValue(x => x.SelectedFile, x => x.Message.IsVisible)
|
||||||
.Select(x => x is { Item1.Type: FileType.Directory, Item2: false });
|
.Select(x => x is { Item1.Type: FileType.Directory, Item2: false });
|
||||||
|
|
||||||
OpenCommand = ReactiveCommand.Create(() => { SelectedPath = SelectedFile != null ? SelectedFile!.Name : "/"; },
|
OpenCommand = ReactiveCommand.Create(() =>
|
||||||
|
{
|
||||||
|
if (".." == SelectedFile?.Name)
|
||||||
|
SelectedPath = SelectedFile?.Parent;
|
||||||
|
else
|
||||||
|
SelectedPath = SelectedFile != null ? SelectedFile!.Name : "/";
|
||||||
|
},
|
||||||
isDirectory, RxApp.MainThreadScheduler);
|
isDirectory, RxApp.MainThreadScheduler);
|
||||||
|
|
||||||
OpenCommand.ThrownExceptions.ObserveOn(RxApp.MainThreadScheduler)
|
OpenCommand.ThrownExceptions.ObserveOn(RxApp.MainThreadScheduler)
|
||||||
@@ -271,7 +311,7 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
{
|
{
|
||||||
ShowWorkingMessage("Switching context...");
|
ShowWorkingMessage("Switching context...");
|
||||||
Namespaces = new List<Namespace>();
|
Namespaces = new List<Namespace>();
|
||||||
kubernetesService.SwitchClusterContext(context!);
|
kubernetesService.SwitchClusterContext(context);
|
||||||
var namespaces = await kubernetesService.GetNamespacesAsync();
|
var namespaces = await kubernetesService.GetNamespacesAsync();
|
||||||
HideWorkingMessage();
|
HideWorkingMessage();
|
||||||
return namespaces;
|
return namespaces;
|
||||||
@@ -285,6 +325,30 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IList<FileInformation> GetFileInformation(IKubernetesService kubernetesService,
|
||||||
|
string path, Pod pod, Namespace nameSpace, Container container)
|
||||||
|
{
|
||||||
|
var kubernetesFileInformation = kubernetesService.GetFiles(
|
||||||
|
nameSpace.Name, pod.Name, container.Name, path);
|
||||||
|
|
||||||
|
// when the path is root, we don't want to show the parent directory
|
||||||
|
if (SelectedPath is not { Length: > 1 }) return kubernetesFileInformation;
|
||||||
|
|
||||||
|
// add the parent directory
|
||||||
|
var parent = SelectedPath[..SelectedPath.LastIndexOf('/')];
|
||||||
|
if (string.IsNullOrEmpty(parent))
|
||||||
|
{
|
||||||
|
parent = "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
return kubernetesFileInformation.Prepend(new FileInformation
|
||||||
|
{
|
||||||
|
Name = "..",
|
||||||
|
Type = FileType.Directory,
|
||||||
|
Parent = parent
|
||||||
|
}).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
private void ShowWorkingMessage(string message)
|
private void ShowWorkingMessage(string message)
|
||||||
{
|
{
|
||||||
Message = new Message
|
Message = new Message
|
||||||
|
|||||||
@@ -72,12 +72,24 @@
|
|||||||
</ListBox>
|
</ListBox>
|
||||||
<GridSplitter Grid.Column="1" ResizeDirection="Columns" />
|
<GridSplitter Grid.Column="1" ResizeDirection="Columns" />
|
||||||
<Grid Grid.Column="2" RowDefinitions="Auto, *">
|
<Grid Grid.Column="2" RowDefinitions="Auto, *">
|
||||||
<Grid ColumnDefinitions="*, Auto">
|
<Grid ColumnDefinitions="*, Auto, Auto">
|
||||||
<StackPanel Grid.Column="0" Orientation="Horizontal">
|
<StackPanel Grid.Column="0" Orientation="Horizontal">
|
||||||
<TextBlock Text="Current Directory" VerticalAlignment="Center" Margin="10 0 0 0"></TextBlock>
|
<TextBlock Text="Current Directory" VerticalAlignment="Center" Margin="10 0 0 0"></TextBlock>
|
||||||
<TextBlock Text="{Binding SelectedPath}" VerticalAlignment="Center" Margin="10 0 0 0"></TextBlock>
|
<TextBlock Text="{Binding SelectedPath}" VerticalAlignment="Center" Margin="10 0 0 0"></TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Grid.Column="1" Orientation="Horizontal" Spacing="4" Margin="10" HorizontalAlignment="Right">
|
<StackPanel Grid.Column="1" VerticalAlignment="Center" Orientation="Horizontal">
|
||||||
|
<Label VerticalAlignment="Center"
|
||||||
|
Margin="0 0 10 0">
|
||||||
|
Container:
|
||||||
|
</Label>
|
||||||
|
<ComboBox ItemsSource="{Binding Containers}"
|
||||||
|
SelectedItem="{Binding SelectedContainer}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
MinWidth="200"
|
||||||
|
Margin="0 0 10 0">
|
||||||
|
</ComboBox>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Grid.Column="2" Orientation="Horizontal" Spacing="4" Margin="10" HorizontalAlignment="Right">
|
||||||
|
|
||||||
<Button Command="{Binding ParentCommand}" VerticalAlignment="Center" ToolTip.Tip="Go To Parent Directory">
|
<Button Command="{Binding ParentCommand}" VerticalAlignment="Center" ToolTip.Tip="Go To Parent Directory">
|
||||||
<PathIcon Data="{StaticResource arrow_curve_up_left_regular}"></PathIcon>
|
<PathIcon Data="{StaticResource arrow_curve_up_left_regular}"></PathIcon>
|
||||||
|
|||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2023 Andreas Billmann
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
Reference in New Issue
Block a user