Add Explorer Workbench with hierarchical, off-UI Storage analysis.

Storage queries run in the background with cancellation and covering indexes so switching views no longer freezes the UI.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-22 12:43:05 +02:00
commit e9aba73552
130 changed files with 15110 additions and 0 deletions

View File

@@ -0,0 +1,86 @@
using Explorer.Analysis;
using Explorer.Application;
using Explorer.Domain;
using Explorer.Domain.Abstractions;
using Explorer.FileOperations;
using Explorer.Indexing;
using Explorer.Plugin.Abstractions;
using Explorer.Plugin.OneDrive;
using Explorer.Presentation.ViewModels;
using Explorer.Search;
using Explorer.Storage.Sqlite;
using Explorer.Windows;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace Explorer.App;
public static class AppServices
{
public static IServiceCollection AddExplorer(this IServiceCollection services)
{
services.AddSingleton<IClock, SystemClock>();
services.AddSingleton<IAppEnvironment, WindowsAppEnvironment>();
services.AddSingleton<IVolumeService, WindowsVolumeService>();
services.AddSingleton<IFileSystemEnumerator, WindowsFileSystemEnumerator>();
services.AddSingleton<IUsnJournal, WindowsUsnJournal>();
services.AddSingleton<IShellFileOperations, WindowsShellFileOperations>();
services.AddSingleton<IOsClipboard, Services.WpfClipboard>();
services.AddSingleton<IIndexStore>(sp =>
{
var env = sp.GetRequiredService<IAppEnvironment>();
var logger = sp.GetRequiredService<ILogger<SqliteIndexStore>>();
return new SqliteIndexStore(env.DatabasePath, logger);
});
services.AddSingleton<IStorageProvider, OneDriveStorageProvider>();
services.AddSingleton<StorageProviderRegistry>();
services.AddSingleton<IHydrationGuard, HydrationGuard>();
services.AddSingleton<SourceManager>();
services.AddSingleton<PathHistoryStore>();
services.AddSingleton<CloudPlaceStore>();
services.AddSingleton<BrowseService>();
services.AddSingleton<FilesystemScanner>();
services.AddSingleton<FolderReconciler>();
services.AddSingleton<UsnChangeApplier>();
services.AddSingleton<IndexingCoordinator>();
services.AddSingleton<DirectoryWatcherHub>();
services.AddSingleton<SearchService>();
services.AddSingleton<AnalysisService>();
services.AddSingleton<TransferQueue>();
services.AddSingleton<FileOperationService>();
services.AddSingleton<DuplicateHashWorker>();
services.AddSingleton<HistoryRollupService>();
services.AddSingleton<MainViewModel>();
services.AddSingleton<MainWindow>();
services.AddHostedService(sp => sp.GetRequiredService<IndexingCoordinator>());
services.AddHostedService(sp => sp.GetRequiredService<TransferQueue>());
services.AddHostedService(sp => sp.GetRequiredService<DuplicateHashWorker>());
services.AddHostedService(sp => sp.GetRequiredService<HistoryRollupService>());
services.AddHostedService<WatcherHostedService>();
return services;
}
}
public sealed class WatcherHostedService : BackgroundService
{
private readonly DirectoryWatcherHub _hub;
private readonly SourceManager _sources;
public WatcherHostedService(DirectoryWatcherHub hub, SourceManager sources)
{
_hub = hub;
_sources = sources;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
using var timer = new PeriodicTimer(TimeSpan.FromSeconds(20));
await _hub.RefreshAsync(stoppingToken).ConfigureAwait(false);
while (await timer.WaitForNextTickAsync(stoppingToken).ConfigureAwait(false))
{
await _sources.RefreshOnlineStateAsync(stoppingToken).ConfigureAwait(false);
await _hub.RefreshAsync(stoppingToken).ConfigureAwait(false);
}
}
}