feat(notes): notes get their own list, a day log and an Enter chain
The editor was reachable from one button that only appeared in My Day, which is how it got forgotten. It is now a permanent sidebar entry next to My Day and Planned, and it fills the tasks island rather than the details pane, because a note list is content. Inside, the date picker is gone: today sits at the top with the capture box, older days follow as dated groups. Enter in a note saves it and opens the next one, Shift+Enter breaks the line so notes can be several, Backspace on an empty note deletes it and steps back up. Deleting also has a visible hover button instead of only "clear the text and hope". Agent-written notes carry a marker. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
ae0c7d3771
commit
0f04b796f4
@@ -9,6 +9,7 @@ using ClaudeDo.Data.Models;
|
||||
using ClaudeDo.Data.Repositories;
|
||||
using ClaudeDo.Ui.Localization;
|
||||
using ClaudeDo.Ui.Services;
|
||||
using ClaudeDo.Ui.Services.Interfaces;
|
||||
using ClaudeDo.Ui.ViewModels.Modals;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;
|
||||
@@ -62,18 +63,10 @@ public sealed partial class TasksIslandViewModel : ViewModelBase, IDisposable
|
||||
|
||||
public event EventHandler? FocusAddTaskRequested;
|
||||
public event EventHandler? TasksChanged;
|
||||
public event Action? NotesRequested;
|
||||
public event Action? PrepRequested;
|
||||
public event Action<string>? ErrorReported;
|
||||
public void RequestFocusAddTask() => FocusAddTaskRequested?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
[RelayCommand]
|
||||
private void OpenNotes()
|
||||
{
|
||||
SelectFrom(null, "notes");
|
||||
NotesRequested?.Invoke();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void ShowPrepLog() => PrepRequested?.Invoke();
|
||||
|
||||
@@ -118,8 +111,17 @@ public sealed partial class TasksIslandViewModel : ViewModelBase, IDisposable
|
||||
private bool _hasCompleted;
|
||||
[ObservableProperty] private bool _showOpenLabel;
|
||||
[ObservableProperty] private string _completedHeader = "";
|
||||
[ObservableProperty] private bool _showNotesRow;
|
||||
[ObservableProperty] private bool _isMyDayList;
|
||||
|
||||
/// <summary>The notes list is content, not a task list — the island swaps its rows for the
|
||||
/// notes editor instead of showing an empty task list.</summary>
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(IsTasksEmptyHintVisible))]
|
||||
[NotifyPropertyChangedFor(nameof(IsTasksEmptyRepoHintVisible))]
|
||||
private bool _isNotesList;
|
||||
|
||||
/// <summary>Null only in tests that construct the island without a notes API.</summary>
|
||||
public NotesEditorViewModel? Notes { get; }
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(IsTasksEmptyRepoHintVisible))]
|
||||
private bool _isLetClaudeVisible;
|
||||
@@ -128,7 +130,7 @@ public sealed partial class TasksIslandViewModel : ViewModelBase, IDisposable
|
||||
|
||||
/// <summary>No visible tasks below the add-task row — every item lands in one of
|
||||
/// Overdue/Open/Completed, so all-false here always means the list has zero items.</summary>
|
||||
public bool IsTasksEmptyHintVisible => !HasOverdue && !HasOpen && !HasCompleted;
|
||||
public bool IsTasksEmptyHintVisible => !HasOverdue && !HasOpen && !HasCompleted && !IsNotesList;
|
||||
|
||||
/// <summary>Extra empty-state line for a User list with no linked working dir, where the
|
||||
/// execution features (queue, Let Claude handle it, Quick session) are unavailable. Derived
|
||||
@@ -182,10 +184,20 @@ public sealed partial class TasksIslandViewModel : ViewModelBase, IDisposable
|
||||
|
||||
private readonly EventHandler _langChangedHandler;
|
||||
|
||||
public TasksIslandViewModel(IDbContextFactory<ClaudeDoDbContext> dbFactory, IWorkerClient? worker = null)
|
||||
public TasksIslandViewModel(
|
||||
IDbContextFactory<ClaudeDoDbContext> dbFactory,
|
||||
IWorkerClient? worker = null,
|
||||
INotesApi? notesApi = null)
|
||||
{
|
||||
_dbFactory = dbFactory;
|
||||
_worker = worker;
|
||||
if (notesApi is not null)
|
||||
{
|
||||
Notes = new NotesEditorViewModel(notesApi);
|
||||
Notes.ErrorReported += msg => ErrorReported?.Invoke(msg);
|
||||
if (worker is not null)
|
||||
worker.NotesUpdatedEvent += () => { if (IsNotesList) _ = Notes.LoadAsync(); };
|
||||
}
|
||||
_completedHeaderRow = new() { ActionCommand = ClearCompletedCommand };
|
||||
CompletedHeader = Loc.T("vm.tasksIsland.completedHeader");
|
||||
if (_worker is not null)
|
||||
@@ -417,14 +429,14 @@ public sealed partial class TasksIslandViewModel : ViewModelBase, IDisposable
|
||||
HasOpen = false;
|
||||
HasCompleted = false;
|
||||
ShowOpenLabel = false;
|
||||
ShowNotesRow = false;
|
||||
if (listChanged) SelectFrom(null, "list-change");
|
||||
if (list is null) { IsLetClaudeVisible = false; IsQuickClaudeVisible = false; IsMergeReviewsVisible = false; LoadTask = Task.CompletedTask; return; }
|
||||
|
||||
HeaderTitle = list.Name;
|
||||
HeaderEyebrow = DateTime.Now.ToString("dddd · MMM dd", CultureInfo.InvariantCulture).ToUpperInvariant();
|
||||
ShowNotesRow = list.Id == "smart:my-day";
|
||||
IsMyDayList = list.Id == "smart:my-day";
|
||||
IsNotesList = list.Id == ListsIslandViewModel.NotesListId;
|
||||
if (IsNotesList && Notes is not null) _ = Notes.LoadAsync();
|
||||
IsLetClaudeVisible = list.Kind == ListKind.User && !string.IsNullOrWhiteSpace(list.WorkingDir);
|
||||
IsQuickClaudeVisible = IsLetClaudeVisible;
|
||||
IsMergeReviewsVisible = list.Id == "virtual:review";
|
||||
|
||||
Reference in New Issue
Block a user