refactor: extract interfaces to Interfaces folders and consolidate filters
Move interface declarations into per-area Interfaces/ subfolders, merge the small task-list filter classes into StatusFilter/SmartFlagFilter, and simplify related services, converters and hub DTO handling. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -7,17 +7,23 @@ namespace ClaudeDo.Ui.Converters;
|
||||
|
||||
public sealed class DiffLineKindToBrushConverter : IValueConverter
|
||||
{
|
||||
private static readonly ISolidColorBrush Added = new SolidColorBrush(Color.Parse("#66BB6A"));
|
||||
private static readonly ISolidColorBrush Removed = new SolidColorBrush(Color.Parse("#EF5350"));
|
||||
private static readonly ISolidColorBrush Hunk = new SolidColorBrush(Color.Parse("#42A5F5"));
|
||||
private static readonly ISolidColorBrush Header = new SolidColorBrush(Color.Parse("#9E9E9E"));
|
||||
private static readonly ISolidColorBrush Default = new SolidColorBrush(Color.Parse("#CFD8DC"));
|
||||
|
||||
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) =>
|
||||
value is WorktreeDiffLineKind kind
|
||||
? kind switch
|
||||
{
|
||||
WorktreeDiffLineKind.Added => new SolidColorBrush(Color.Parse("#66BB6A")),
|
||||
WorktreeDiffLineKind.Removed => new SolidColorBrush(Color.Parse("#EF5350")),
|
||||
WorktreeDiffLineKind.Hunk => new SolidColorBrush(Color.Parse("#42A5F5")),
|
||||
WorktreeDiffLineKind.Header => new SolidColorBrush(Color.Parse("#9E9E9E")),
|
||||
_ => new SolidColorBrush(Color.Parse("#CFD8DC")),
|
||||
WorktreeDiffLineKind.Added => Added,
|
||||
WorktreeDiffLineKind.Removed => Removed,
|
||||
WorktreeDiffLineKind.Hunk => Hunk,
|
||||
WorktreeDiffLineKind.Header => Header,
|
||||
_ => Default,
|
||||
}
|
||||
: new SolidColorBrush(Color.Parse("#CFD8DC"));
|
||||
: Default;
|
||||
|
||||
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
=> throw new NotSupportedException();
|
||||
|
||||
@@ -7,17 +7,23 @@ namespace ClaudeDo.Ui.Converters;
|
||||
|
||||
public sealed class WorktreeStateColorConverter : IValueConverter
|
||||
{
|
||||
private static readonly ISolidColorBrush Active = new SolidColorBrush(Color.Parse("#42A5F5"));
|
||||
private static readonly ISolidColorBrush Merged = new SolidColorBrush(Color.Parse("#66BB6A"));
|
||||
private static readonly ISolidColorBrush Discarded = new SolidColorBrush(Color.Parse("#9E9E9E"));
|
||||
private static readonly ISolidColorBrush Kept = new SolidColorBrush(Color.Parse("#FFA726"));
|
||||
private static readonly ISolidColorBrush Default = new SolidColorBrush(Colors.Gray);
|
||||
|
||||
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) =>
|
||||
value is WorktreeState state
|
||||
? state switch
|
||||
{
|
||||
WorktreeState.Active => new SolidColorBrush(Color.Parse("#42A5F5")),
|
||||
WorktreeState.Merged => new SolidColorBrush(Color.Parse("#66BB6A")),
|
||||
WorktreeState.Discarded => new SolidColorBrush(Color.Parse("#9E9E9E")),
|
||||
WorktreeState.Kept => new SolidColorBrush(Color.Parse("#FFA726")),
|
||||
_ => new SolidColorBrush(Colors.Gray),
|
||||
WorktreeState.Active => Active,
|
||||
WorktreeState.Merged => Merged,
|
||||
WorktreeState.Discarded => Discarded,
|
||||
WorktreeState.Kept => Kept,
|
||||
_ => Default,
|
||||
}
|
||||
: new SolidColorBrush(Colors.Gray);
|
||||
: Default;
|
||||
|
||||
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
=> throw new NotSupportedException();
|
||||
|
||||
49
src/ClaudeDo.Ui/Services/InstallArtifactLocator.cs
Normal file
49
src/ClaudeDo.Ui/Services/InstallArtifactLocator.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
namespace ClaudeDo.Ui.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Locates an executable inside a ClaudeDo install: walk up from the running
|
||||
/// directory to the folder containing install.json, otherwise read the
|
||||
/// uninstall registry key. Subclasses supply the subdirectory and exe name.
|
||||
/// </summary>
|
||||
public abstract class InstallArtifactLocator
|
||||
{
|
||||
private const string InstallJson = "install.json";
|
||||
|
||||
protected abstract string Subdir { get; }
|
||||
protected abstract string ExeName { get; }
|
||||
|
||||
public string? Find()
|
||||
=> FindByWalkingUp(AppContext.BaseDirectory)
|
||||
?? (OperatingSystem.IsWindows() ? FindByRegistry() : null);
|
||||
|
||||
public string? FindByWalkingUp(string startDir)
|
||||
{
|
||||
var dir = new DirectoryInfo(startDir);
|
||||
while (dir is not null)
|
||||
{
|
||||
if (File.Exists(Path.Combine(dir.FullName, InstallJson)))
|
||||
{
|
||||
var candidate = Path.Combine(dir.FullName, Subdir, ExeName);
|
||||
return File.Exists(candidate) ? candidate : null;
|
||||
}
|
||||
dir = dir.Parent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
[System.Runtime.Versioning.SupportedOSPlatform("windows")]
|
||||
public string? FindByRegistry()
|
||||
{
|
||||
if (!OperatingSystem.IsWindows()) return null;
|
||||
try
|
||||
{
|
||||
using var key = Microsoft.Win32.Registry.LocalMachine
|
||||
.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Uninstall\ClaudeDo");
|
||||
var location = key?.GetValue("InstallLocation") as string;
|
||||
if (string.IsNullOrEmpty(location)) return null;
|
||||
var candidate = Path.Combine(location, Subdir, ExeName);
|
||||
return File.Exists(candidate) ? candidate : null;
|
||||
}
|
||||
catch { return null; }
|
||||
}
|
||||
}
|
||||
@@ -1,48 +1,7 @@
|
||||
namespace ClaudeDo.Ui.Services;
|
||||
|
||||
public sealed class InstallerLocator
|
||||
public sealed class InstallerLocator : InstallArtifactLocator
|
||||
{
|
||||
private const string InstallJson = "install.json";
|
||||
private const string InstallerExe = "ClaudeDo.Installer.exe";
|
||||
private const string UninstallerSubdir = "uninstaller";
|
||||
|
||||
public string? Find()
|
||||
=> FindByWalkingUp(AppContext.BaseDirectory)
|
||||
?? (OperatingSystem.IsWindows() ? FindByRegistry() : null);
|
||||
|
||||
public string? FindByWalkingUp(string startDir)
|
||||
{
|
||||
var dir = new DirectoryInfo(startDir);
|
||||
while (dir is not null)
|
||||
{
|
||||
var manifest = Path.Combine(dir.FullName, InstallJson);
|
||||
if (File.Exists(manifest))
|
||||
{
|
||||
var candidate = Path.Combine(dir.FullName, UninstallerSubdir, InstallerExe);
|
||||
return File.Exists(candidate) ? candidate : null;
|
||||
}
|
||||
dir = dir.Parent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
[System.Runtime.Versioning.SupportedOSPlatform("windows")]
|
||||
public string? FindByRegistry()
|
||||
{
|
||||
if (!OperatingSystem.IsWindows()) return null;
|
||||
|
||||
try
|
||||
{
|
||||
using var key = Microsoft.Win32.Registry.LocalMachine
|
||||
.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Uninstall\ClaudeDo");
|
||||
var location = key?.GetValue("InstallLocation") as string;
|
||||
if (string.IsNullOrEmpty(location)) return null;
|
||||
var candidate = Path.Combine(location, UninstallerSubdir, InstallerExe);
|
||||
return File.Exists(candidate) ? candidate : null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
protected override string Subdir => "uninstaller";
|
||||
protected override string ExeName => "ClaudeDo.Installer.exe";
|
||||
}
|
||||
|
||||
8
src/ClaudeDo.Ui/Services/Interfaces/IPrimeScheduleApi.cs
Normal file
8
src/ClaudeDo.Ui/Services/Interfaces/IPrimeScheduleApi.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace ClaudeDo.Ui.Services;
|
||||
|
||||
public interface IPrimeScheduleApi
|
||||
{
|
||||
Task<List<PrimeScheduleDto>> ListAsync();
|
||||
Task<PrimeScheduleDto?> UpsertAsync(PrimeScheduleDto dto);
|
||||
Task DeleteAsync(Guid id);
|
||||
}
|
||||
@@ -226,6 +226,13 @@ public partial class WorkerClient : ObservableObject, IAsyncDisposable, IWorkerC
|
||||
try { await _hub.StopAsync(); } catch { /* swallow */ }
|
||||
}
|
||||
|
||||
/// <summary>Invoke a hub method, returning default (null) when the worker is offline or errors.</summary>
|
||||
private async Task<T?> TryInvokeAsync<T>(string method, params object?[] args)
|
||||
{
|
||||
try { return await _hub.InvokeCoreAsync<T>(method, args); }
|
||||
catch { return default; }
|
||||
}
|
||||
|
||||
public async Task RunNowAsync(string taskId)
|
||||
{
|
||||
RunNowRequestedEvent?.Invoke(taskId);
|
||||
@@ -248,17 +255,8 @@ public partial class WorkerClient : ObservableObject, IAsyncDisposable, IWorkerC
|
||||
"MergeTask", taskId, targetBranch, removeWorktree, commitMessage);
|
||||
}
|
||||
|
||||
public async Task<MergeTargetsDto?> GetMergeTargetsAsync(string taskId)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _hub.InvokeAsync<MergeTargetsDto>("GetMergeTargets", taskId);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public Task<MergeTargetsDto?> GetMergeTargetsAsync(string taskId)
|
||||
=> TryInvokeAsync<MergeTargetsDto>("GetMergeTargets", taskId);
|
||||
|
||||
public async Task CancelTaskAsync(string taskId)
|
||||
{
|
||||
@@ -271,34 +269,15 @@ public partial class WorkerClient : ObservableObject, IAsyncDisposable, IWorkerC
|
||||
}
|
||||
|
||||
public async Task<List<AgentInfo>> GetAgentsAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var agents = await _hub.InvokeAsync<List<AgentInfo>>("GetAgents");
|
||||
return agents ?? [];
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
=> await TryInvokeAsync<List<AgentInfo>>("GetAgents") ?? [];
|
||||
|
||||
public async Task RefreshAgentsAsync()
|
||||
{
|
||||
await _hub.InvokeAsync("RefreshAgents");
|
||||
}
|
||||
|
||||
public async Task<SeedResultDto?> RestoreDefaultAgentsAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _hub.InvokeAsync<SeedResultDto>("RestoreDefaultAgents");
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public Task<SeedResultDto?> RestoreDefaultAgentsAsync()
|
||||
=> TryInvokeAsync<SeedResultDto>("RestoreDefaultAgents");
|
||||
|
||||
private async Task SeedActiveTasksAsync()
|
||||
{
|
||||
@@ -329,17 +308,8 @@ public partial class WorkerClient : ObservableObject, IAsyncDisposable, IWorkerC
|
||||
await _hub.DisposeAsync();
|
||||
}
|
||||
|
||||
public async Task<AppSettingsDto?> GetAppSettingsAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _hub.InvokeAsync<AppSettingsDto>("GetAppSettings");
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public Task<AppSettingsDto?> GetAppSettingsAsync()
|
||||
=> TryInvokeAsync<AppSettingsDto>("GetAppSettings");
|
||||
|
||||
public async Task UpdateAppSettingsAsync(AppSettingsDto dto)
|
||||
{
|
||||
@@ -347,16 +317,10 @@ public partial class WorkerClient : ObservableObject, IAsyncDisposable, IWorkerC
|
||||
}
|
||||
|
||||
public async Task<List<PrimeScheduleDto>> GetPrimeSchedulesAsync()
|
||||
{
|
||||
try { return await _hub.InvokeAsync<List<PrimeScheduleDto>>("ListPrimeSchedules"); }
|
||||
catch { return new List<PrimeScheduleDto>(); }
|
||||
}
|
||||
=> await TryInvokeAsync<List<PrimeScheduleDto>>("ListPrimeSchedules") ?? new List<PrimeScheduleDto>();
|
||||
|
||||
public async Task<PrimeScheduleDto?> UpsertPrimeScheduleAsync(PrimeScheduleDto dto)
|
||||
{
|
||||
try { return await _hub.InvokeAsync<PrimeScheduleDto>("UpsertPrimeSchedule", dto); }
|
||||
catch { return null; }
|
||||
}
|
||||
public Task<PrimeScheduleDto?> UpsertPrimeScheduleAsync(PrimeScheduleDto dto)
|
||||
=> TryInvokeAsync<PrimeScheduleDto>("UpsertPrimeSchedule", dto);
|
||||
|
||||
public async Task DeletePrimeScheduleAsync(Guid id)
|
||||
{
|
||||
@@ -374,17 +338,8 @@ public partial class WorkerClient : ObservableObject, IAsyncDisposable, IWorkerC
|
||||
await _hub.InvokeAsync("UpdateListConfig", dto);
|
||||
}
|
||||
|
||||
public async Task<ListConfigDto?> GetListConfigAsync(string listId)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _hub.InvokeAsync<ListConfigDto?>("GetListConfig", listId);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public Task<ListConfigDto?> GetListConfigAsync(string listId)
|
||||
=> TryInvokeAsync<ListConfigDto>("GetListConfig", listId);
|
||||
|
||||
public async Task UpdateTaskAgentSettingsAsync(UpdateTaskAgentSettingsDto dto)
|
||||
{
|
||||
@@ -396,42 +351,15 @@ public partial class WorkerClient : ObservableObject, IAsyncDisposable, IWorkerC
|
||||
await _hub.InvokeAsync("SetTaskStatus", taskId, status.ToString());
|
||||
}
|
||||
|
||||
public async Task<WorktreeCleanupDto?> CleanupFinishedWorktreesAsync(string? listId = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _hub.InvokeAsync<WorktreeCleanupDto>("CleanupFinishedWorktrees", listId);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public Task<WorktreeCleanupDto?> CleanupFinishedWorktreesAsync(string? listId = null)
|
||||
=> TryInvokeAsync<WorktreeCleanupDto>("CleanupFinishedWorktrees", listId);
|
||||
|
||||
public async Task<WorktreeResetDto?> ResetAllWorktreesAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _hub.InvokeAsync<WorktreeResetDto>("ResetAllWorktrees");
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public Task<WorktreeResetDto?> ResetAllWorktreesAsync()
|
||||
=> TryInvokeAsync<WorktreeResetDto>("ResetAllWorktrees");
|
||||
|
||||
public async Task<List<WorktreeOverviewDto>> GetWorktreesOverviewAsync(string? listId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var rows = await _hub.InvokeAsync<List<WorktreeOverviewDto>>("GetWorktreesOverview", listId);
|
||||
return rows ?? new List<WorktreeOverviewDto>();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return new List<WorktreeOverviewDto>();
|
||||
}
|
||||
}
|
||||
=> await TryInvokeAsync<List<WorktreeOverviewDto>>("GetWorktreesOverview", listId)
|
||||
?? new List<WorktreeOverviewDto>();
|
||||
|
||||
public async Task<(bool Ok, string? Error)> SetWorktreeStateAsync(string taskId, WorktreeState newState)
|
||||
{
|
||||
@@ -450,17 +378,8 @@ public partial class WorkerClient : ObservableObject, IAsyncDisposable, IWorkerC
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ForceRemoveResultDto?> ForceRemoveWorktreeAsync(string taskId)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _hub.InvokeAsync<ForceRemoveResultDto>("ForceRemoveWorktree", taskId);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public Task<ForceRemoveResultDto?> ForceRemoveWorktreeAsync(string taskId)
|
||||
=> TryInvokeAsync<ForceRemoveResultDto>("ForceRemoveWorktree", taskId);
|
||||
|
||||
public async Task<PlanningSessionStartInfo> StartPlanningSessionAsync(string taskId, CancellationToken ct = default)
|
||||
=> await _hub.InvokeAsync<PlanningSessionStartInfo>("StartPlanningSessionAsync", taskId, ct);
|
||||
@@ -481,29 +400,10 @@ public partial class WorkerClient : ObservableObject, IAsyncDisposable, IWorkerC
|
||||
=> await _hub.InvokeAsync<int>("GetPendingDraftCountAsync", taskId, ct);
|
||||
|
||||
public async Task<IReadOnlyList<SubtaskDiffDto>> GetPlanningAggregateAsync(string planningTaskId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _hub.InvokeAsync<List<SubtaskDiffDto>>("GetPlanningAggregate", planningTaskId);
|
||||
return result ?? [];
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
=> await TryInvokeAsync<List<SubtaskDiffDto>>("GetPlanningAggregate", planningTaskId) ?? [];
|
||||
|
||||
public async Task<CombinedDiffResultDto?> BuildPlanningIntegrationBranchAsync(string planningTaskId, string targetBranch)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _hub.InvokeAsync<CombinedDiffResultDto>("BuildPlanningIntegrationBranch", planningTaskId, targetBranch);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public Task<CombinedDiffResultDto?> BuildPlanningIntegrationBranchAsync(string planningTaskId, string targetBranch)
|
||||
=> TryInvokeAsync<CombinedDiffResultDto>("BuildPlanningIntegrationBranch", planningTaskId, targetBranch);
|
||||
|
||||
public async Task MergeAllPlanningAsync(string planningTaskId, string targetBranch)
|
||||
{
|
||||
|
||||
@@ -1,43 +1,7 @@
|
||||
namespace ClaudeDo.Ui.Services;
|
||||
|
||||
public sealed class WorkerLocator
|
||||
public sealed class WorkerLocator : InstallArtifactLocator
|
||||
{
|
||||
private const string InstallJson = "install.json";
|
||||
private const string WorkerExe = "ClaudeDo.Worker.exe";
|
||||
private const string WorkerSubdir = "worker";
|
||||
|
||||
public string? Find()
|
||||
=> FindByWalkingUp(AppContext.BaseDirectory)
|
||||
?? (OperatingSystem.IsWindows() ? FindByRegistry() : null);
|
||||
|
||||
public string? FindByWalkingUp(string startDir)
|
||||
{
|
||||
var dir = new DirectoryInfo(startDir);
|
||||
while (dir is not null)
|
||||
{
|
||||
if (File.Exists(Path.Combine(dir.FullName, InstallJson)))
|
||||
{
|
||||
var candidate = Path.Combine(dir.FullName, WorkerSubdir, WorkerExe);
|
||||
return File.Exists(candidate) ? candidate : null;
|
||||
}
|
||||
dir = dir.Parent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
[System.Runtime.Versioning.SupportedOSPlatform("windows")]
|
||||
public string? FindByRegistry()
|
||||
{
|
||||
if (!OperatingSystem.IsWindows()) return null;
|
||||
try
|
||||
{
|
||||
using var key = Microsoft.Win32.Registry.LocalMachine
|
||||
.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Uninstall\ClaudeDo");
|
||||
var location = key?.GetValue("InstallLocation") as string;
|
||||
if (string.IsNullOrEmpty(location)) return null;
|
||||
var candidate = Path.Combine(location, WorkerSubdir, WorkerExe);
|
||||
return File.Exists(candidate) ? candidate : null;
|
||||
}
|
||||
catch { return null; }
|
||||
}
|
||||
protected override string Subdir => "worker";
|
||||
protected override string ExeName => "ClaudeDo.Worker.exe";
|
||||
}
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
namespace ClaudeDo.Ui.Services;
|
||||
|
||||
public interface IPrimeScheduleApi
|
||||
{
|
||||
Task<List<PrimeScheduleDto>> ListAsync();
|
||||
Task<PrimeScheduleDto?> UpsertAsync(PrimeScheduleDto dto);
|
||||
Task DeleteAsync(Guid id);
|
||||
}
|
||||
|
||||
public sealed class WorkerPrimeScheduleApi : IPrimeScheduleApi
|
||||
{
|
||||
private readonly WorkerClient _client;
|
||||
Reference in New Issue
Block a user