docs: add subtask tree view design spec
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
118
docs/superpowers/specs/2026-04-16-subtask-tree-view-design.md
Normal file
118
docs/superpowers/specs/2026-04-16-subtask-tree-view-design.md
Normal file
@@ -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<SubtaskItemViewModel> 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<Dictionary<string, int>> GetCountsByTaskIdsAsync(IEnumerable<string> 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)
|
||||
Reference in New Issue
Block a user