Initial commit to new repository
This commit is contained in:
129
src/AxCopilot/Services/TaskRunStore.cs
Normal file
129
src/AxCopilot/Services/TaskRunStore.cs
Normal file
@@ -0,0 +1,129 @@
|
||||
namespace AxCopilot.Services;
|
||||
|
||||
/// <summary>
|
||||
/// 실행 중/최근 task 상태를 관리하는 전용 저장소.
|
||||
/// AppStateService와 UI가 같은 로직을 공유할 수 있게 분리합니다.
|
||||
/// </summary>
|
||||
public sealed class TaskRunStore
|
||||
{
|
||||
public sealed class TaskRun
|
||||
{
|
||||
public string Id { get; init; } = "";
|
||||
public string Kind { get; set; } = "";
|
||||
public string Title { get; set; } = "";
|
||||
public string Status { get; set; } = "running";
|
||||
public string Summary { get; set; } = "";
|
||||
public DateTime StartedAt { get; set; } = DateTime.Now;
|
||||
public DateTime UpdatedAt { get; set; } = DateTime.Now;
|
||||
public string? FilePath { get; set; }
|
||||
}
|
||||
|
||||
private readonly List<TaskRun> _active = new();
|
||||
private readonly List<TaskRun> _recent = new();
|
||||
|
||||
public IReadOnlyList<TaskRun> ActiveTasks => _active;
|
||||
public IReadOnlyList<TaskRun> RecentTasks => _recent;
|
||||
|
||||
public event Action? Changed;
|
||||
|
||||
public void Upsert(string id, string kind, string title, string summary, string status = "running", string? filePath = null)
|
||||
{
|
||||
var existing = _active.FirstOrDefault(t => string.Equals(t.Id, id, StringComparison.OrdinalIgnoreCase));
|
||||
if (existing == null)
|
||||
{
|
||||
_active.Add(new TaskRun
|
||||
{
|
||||
Id = id,
|
||||
Kind = kind,
|
||||
Title = title,
|
||||
Summary = summary,
|
||||
Status = status,
|
||||
FilePath = filePath,
|
||||
StartedAt = DateTime.Now,
|
||||
UpdatedAt = DateTime.Now,
|
||||
});
|
||||
Changed?.Invoke();
|
||||
return;
|
||||
}
|
||||
|
||||
existing.Kind = kind;
|
||||
existing.Title = title;
|
||||
existing.Summary = summary;
|
||||
existing.Status = status;
|
||||
existing.FilePath = filePath;
|
||||
existing.UpdatedAt = DateTime.Now;
|
||||
Changed?.Invoke();
|
||||
}
|
||||
|
||||
public void Complete(string id, string? summary = null, string status = "completed")
|
||||
{
|
||||
var existing = _active.FirstOrDefault(t => string.Equals(t.Id, id, StringComparison.OrdinalIgnoreCase));
|
||||
if (existing == null)
|
||||
return;
|
||||
|
||||
_active.Remove(existing);
|
||||
existing.Status = status;
|
||||
if (!string.IsNullOrWhiteSpace(summary))
|
||||
existing.Summary = summary;
|
||||
existing.UpdatedAt = DateTime.Now;
|
||||
_recent.Insert(0, existing);
|
||||
TrimRecent();
|
||||
Changed?.Invoke();
|
||||
}
|
||||
|
||||
public void CompleteByPrefix(string prefix, string? summary = null, string status = "completed")
|
||||
{
|
||||
var matches = _active
|
||||
.Where(t => t.Id.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
|
||||
.ToList();
|
||||
|
||||
foreach (var match in matches)
|
||||
{
|
||||
_active.Remove(match);
|
||||
match.Status = status;
|
||||
if (!string.IsNullOrWhiteSpace(summary))
|
||||
match.Summary = summary;
|
||||
match.UpdatedAt = DateTime.Now;
|
||||
_recent.Insert(0, match);
|
||||
}
|
||||
|
||||
if (matches.Count == 0)
|
||||
return;
|
||||
|
||||
TrimRecent();
|
||||
Changed?.Invoke();
|
||||
}
|
||||
|
||||
public void RestoreRecent(IEnumerable<TaskRun>? recent)
|
||||
{
|
||||
RestoreState(null, recent);
|
||||
}
|
||||
|
||||
public void RestoreState(IEnumerable<TaskRun>? active, IEnumerable<TaskRun>? recent)
|
||||
{
|
||||
_active.Clear();
|
||||
_recent.Clear();
|
||||
|
||||
if (active != null)
|
||||
{
|
||||
_active.AddRange(active
|
||||
.OrderByDescending(t => t.UpdatedAt)
|
||||
.Take(25));
|
||||
}
|
||||
|
||||
if (recent != null)
|
||||
{
|
||||
_recent.AddRange(recent
|
||||
.OrderByDescending(t => t.UpdatedAt)
|
||||
.Take(25));
|
||||
}
|
||||
|
||||
Changed?.Invoke();
|
||||
}
|
||||
|
||||
private void TrimRecent()
|
||||
{
|
||||
if (_recent.Count > 25)
|
||||
_recent.RemoveRange(25, _recent.Count - 25);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user