feat(ui): add MarkdownView control and editable description in details island
New MarkdownView UserControl renders a markdown preview. Details island gains an editable Description section with edit/preview toggle, collapsible header, copy-to-clipboard, and debounced auto-save (400ms). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -26,9 +26,33 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase
|
||||
|
||||
// Editable fields
|
||||
[ObservableProperty] private string _editableTitle = "";
|
||||
[ObservableProperty] private string _editableDescription = "";
|
||||
[ObservableProperty] private bool _isEditingDescription;
|
||||
[ObservableProperty] private bool _isDescriptionExpanded = true;
|
||||
[ObservableProperty] private string _notes = "";
|
||||
[ObservableProperty] private string _promptInput = "";
|
||||
|
||||
public bool IsDescriptionEditorVisible => IsDescriptionExpanded && IsEditingDescription;
|
||||
public bool IsDescriptionPreviewVisible => IsDescriptionExpanded && !IsEditingDescription;
|
||||
|
||||
partial void OnIsDescriptionExpandedChanged(bool value)
|
||||
{
|
||||
OnPropertyChanged(nameof(IsDescriptionEditorVisible));
|
||||
OnPropertyChanged(nameof(IsDescriptionPreviewVisible));
|
||||
}
|
||||
|
||||
partial void OnIsEditingDescriptionChanged(bool value)
|
||||
{
|
||||
OnPropertyChanged(nameof(IsDescriptionEditorVisible));
|
||||
OnPropertyChanged(nameof(IsDescriptionPreviewVisible));
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void ToggleEditDescription() => IsEditingDescription = !IsEditingDescription;
|
||||
|
||||
[RelayCommand]
|
||||
private void ToggleDescriptionExpanded() => IsDescriptionExpanded = !IsDescriptionExpanded;
|
||||
|
||||
// Short task-id badge, e.g. "#T1A"
|
||||
public string TaskIdBadge => Task != null ? $"#T{Task.Id[..Math.Min(3, Task.Id.Length)].ToUpperInvariant()}" : "";
|
||||
|
||||
@@ -78,6 +102,9 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase
|
||||
private bool _suppressAgentSave;
|
||||
private CancellationTokenSource? _agentSaveCts;
|
||||
|
||||
private bool _suppressDescSave;
|
||||
private CancellationTokenSource? _descSaveCts;
|
||||
|
||||
public bool IsAgentSectionEnabled => !IsRunning;
|
||||
|
||||
[ObservableProperty] private string? _worktreePath;
|
||||
@@ -269,6 +296,31 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase
|
||||
partial void OnTaskSystemPromptChanged(string value) => QueueAgentSave();
|
||||
partial void OnTaskSelectedAgentChanged(AgentInfo? value) => QueueAgentSave();
|
||||
|
||||
partial void OnEditableDescriptionChanged(string value)
|
||||
{
|
||||
if (_suppressDescSave || Task is null) return;
|
||||
_descSaveCts?.Cancel();
|
||||
_descSaveCts = new CancellationTokenSource();
|
||||
_ = SaveDescriptionAsync(_descSaveCts.Token);
|
||||
}
|
||||
|
||||
private async System.Threading.Tasks.Task SaveDescriptionAsync(CancellationToken ct)
|
||||
{
|
||||
try
|
||||
{
|
||||
await System.Threading.Tasks.Task.Delay(400, ct);
|
||||
if (Task is null) return;
|
||||
await using var ctx = _dbFactory.CreateDbContext();
|
||||
var repo = new TaskRepository(ctx);
|
||||
var entity = await repo.GetByIdAsync(Task.Id);
|
||||
if (entity is null) return;
|
||||
entity.Description = string.IsNullOrWhiteSpace(EditableDescription) ? null : EditableDescription;
|
||||
await repo.UpdateAsync(entity);
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
catch { }
|
||||
}
|
||||
|
||||
private void QueueAgentSave()
|
||||
{
|
||||
if (_suppressAgentSave || Task is null) return;
|
||||
@@ -348,6 +400,7 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase
|
||||
{
|
||||
_subscribedTaskId = null;
|
||||
EditableTitle = "";
|
||||
EditableDescription = "";
|
||||
Notes = "";
|
||||
Model = null;
|
||||
WorktreePath = null;
|
||||
@@ -392,6 +445,9 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase
|
||||
if (entity == null) return;
|
||||
|
||||
EditableTitle = entity.Title;
|
||||
_suppressDescSave = true;
|
||||
try { EditableDescription = entity.Description ?? ""; }
|
||||
finally { _suppressDescSave = false; }
|
||||
Notes = entity.Notes ?? "";
|
||||
Model = entity.Model;
|
||||
WorktreePath = entity.Worktree?.Path;
|
||||
|
||||
Reference in New Issue
Block a user