From 4f25c3dd40e6a061ca23ae8fcdeaf89a4bcf5253 Mon Sep 17 00:00:00 2001 From: mika kuns Date: Thu, 16 Apr 2026 10:18:04 +0200 Subject: [PATCH] docs: add subtask tree view design spec Co-Authored-By: Claude Opus 4.6 (1M context) --- .../2026-04-16-subtask-tree-view-design.md | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 docs/superpowers/specs/2026-04-16-subtask-tree-view-design.md diff --git a/docs/superpowers/specs/2026-04-16-subtask-tree-view-design.md b/docs/superpowers/specs/2026-04-16-subtask-tree-view-design.md new file mode 100644 index 0000000..b5b040a --- /dev/null +++ b/docs/superpowers/specs/2026-04-16-subtask-tree-view-design.md @@ -0,0 +1,118 @@ +# Subtask Tree View in Task List + +## Problem + +Subtasks are invisible in the task list — users only see them after opening the detail pane or editor modal. This makes it hard to get an overview of task progress without clicking into each task individually. + +## Solution + +Show subtasks indented below their parent task in the task list, with expand/collapse. Tasks start collapsed with a visual indicator when subtasks exist. + +## Scope + +Pure UI/ViewModel change. No data model changes, no new migrations, no repository schema changes. + +## Design + +### ViewModel Changes + +**TaskItemViewModel** — add: + +- `ObservableCollection Subtasks` — populated on first expand +- `bool IsExpanded` — observable, default `false`; toggles subtask visibility +- `bool HasSubtasks` — observable, set during initial load from a count query +- `int SubtaskCount` — observable, used for the indicator +- `ToggleExpandedCommand` — flips `IsExpanded`; on first expand, loads subtasks from `SubtaskRepository.GetByTaskIdAsync` +- `ToggleSubtaskDoneCommand(string subtaskId)` — toggles a subtask's `Completed` and persists via `SubtaskRepository.UpdateAsync` + +Constructor gains `SubtaskRepository` and initial `subtaskCount` parameter. + +**TaskListViewModel.LoadAsync** — after fetching tasks, run a single batch query to get subtask counts per task. Pass counts into each `TaskItemViewModel`. This avoids N+1 queries on load. + +**TaskListViewModel.RefreshSingleAsync** — if the refreshed task's `IsExpanded` is true, also reload its subtasks from DB and update the collection. + +### Repository Changes + +**SubtaskRepository** — add one method: + +```csharp +Task> GetCountsByTaskIdsAsync(IEnumerable taskIds, CancellationToken ct = default) +``` + +Single query: `SELECT task_id, COUNT(*) FROM subtasks WHERE task_id IN (...) GROUP BY task_id`. Returns a map of taskId -> count. Tasks with no subtasks won't appear in the result (count defaults to 0). + +### XAML Changes + +**TaskListView.axaml** — the `DataTemplate` for `TaskItemViewModel` becomes a 2-row grid: + +``` +Row 0: [ExpandChevron] [StatusCircle] [Title + Tags/Status subtitle] +Row 1: [SubtaskItemsControl, margin-left ~40px, visible when IsExpanded] +``` + +**Row 0 — Expand chevron:** +- Column 0 gets a small chevron button (12x12 `Path` data) before the status circle +- Right-pointing when collapsed, down-pointing when expanded +- Bound to `ToggleExpandedCommand` +- Only visible when `HasSubtasks` is true (via `IsVisible` binding) +- When `HasSubtasks` is false, the space is empty but reserved (fixed-width column) so all titles align + +**Row 1 — Subtask list:** +- `ItemsControl` bound to `Subtasks` +- `IsVisible` bound to `IsExpanded` +- Left margin ~40px for visual indentation +- Each subtask item: `CheckBox` (bound to `Completed`) + `TextBlock` (bound to `Title`) +- Subtask row has its own context menu flyout with "Edit Task" (opens parent task's editor modal via `EditTaskCommand` on root `TaskListViewModel`) +- Checkbox toggle calls `ToggleSubtaskDoneCommand` on the parent `TaskItemViewModel` + +**Column layout change:** The existing 2-column `Grid` (`Auto, *`) gets a third column prepended: `Auto, Auto, *`. The chevron goes in column 0, status circle in column 1, title stack in column 2. Row 1 spans all 3 columns. + +### Subtask Checkbox Interaction + +When a subtask checkbox is toggled in the list: +1. Update the `SubtaskItemViewModel.Completed` property +2. Call `SubtaskRepository.UpdateAsync` with the updated entity (same auto-save pattern as `TaskDetailView`) +3. No need to refresh the parent task — subtask completion doesn't affect task status + +### Subtask Context Menu + +Right-click on a subtask row shows: +- "Edit Task" — opens the parent task's editor modal (same flow as `EditTaskCommand`) + +This reuses the existing editor which already has full subtask editing (add/remove/reorder/rename). + +### Real-time Updates + +When `RefreshSingleAsync` fires (via SignalR `TaskUpdatedEvent`): +1. Reload subtask count, update `HasSubtasks` and `SubtaskCount` +2. If `IsExpanded`, reload subtask list from DB and reconcile with the observable collection + +### Detail Pane Sync + +When the user edits subtasks in `TaskDetailView` (auto-save) or `TaskEditorView` (batch-save), the list view's subtask state may become stale. Two options: + +**Chosen approach:** The detail pane and editor already trigger `TaskUpdatedEvent` (or the editor's save path calls `RefreshSingleAsync` via `SelectedTask.Refresh`). Extend `Refresh` on `TaskItemViewModel` to also reload subtasks if expanded, and update `HasSubtasks`/`SubtaskCount`. + +### Visual Style + +- Chevron: 10x10 path, `TextDimBrush` color, no background, cursor=Hand +- Subtask rows: smaller font (12px), `TextDimBrush` for unchecked title, strikethrough + dimmed for completed +- Subtask checkbox: standard Avalonia `CheckBox` (no custom circular border), small size +- Subtask row vertical padding: 2px (compact) +- Indent: 40px left margin on the subtask `ItemsControl` + +## Files to Modify + +1. `src/ClaudeDo.Data/Repositories/SubtaskRepository.cs` — add `GetCountsByTaskIdsAsync` +2. `src/ClaudeDo.Ui/ViewModels/TaskItemViewModel.cs` — add subtask collection, expand/collapse, toggle done +3. `src/ClaudeDo.Ui/ViewModels/TaskListViewModel.cs` — batch-load counts, pass SubtaskRepository, extend refresh +4. `src/ClaudeDo.Ui/Views/TaskListView.axaml` — restructure item template with chevron + nested ItemsControl +5. `src/ClaudeDo.Ui/Views/TaskListView.axaml.cs` — handle subtask context menu pointer-pressed if needed +6. `src/ClaudeDo.App/Program.cs` — pass SubtaskRepository to TaskListViewModel (if not already available via DI) + +## Out of Scope + +- Drag-to-reorder subtasks in the list view +- Add subtask directly from the list view +- Subtask progress indicator (e.g., "2/5 done") on collapsed tasks +- Recursive task nesting (tasks containing tasks)