Add focus activation, 3-zone drag nesting, and click-to-navigate
Some checks failed
Node.js build / build (22.x) (push) Has been cancelled
Node.js build / build (20.x) (push) Has been cancelled

Implement remaining Round 3 enhancements:
- ArrowDown when panel unfocused activates it at first item (like Outline view)
- 3-zone drag-drop: top/bottom thirds insert above/below, middle third nests as child
- Click on todo text to focus it in editor (onClick callback)
- Dragging parent automatically moves nested children (stopPropagation fix)
- Cross-file move inserts todo below heading with blank line (addBlankLine param)
- Updated CLAUDE.md with sidebar view architecture documentation

Build: 85 tests pass, production build succeeds.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-20 10:17:27 -08:00
parent 349810aecf
commit a00b96231c
8 changed files with 263 additions and 31 deletions

View File

@@ -95,3 +95,49 @@ From AGENTS.md and Obsidian best practices:
- Keep startup lightweight; defer heavy work until needed
- Use stable command IDs (don't rename after release)
- Provide sensible defaults for all settings
## Sidebar View Architecture
### Key Files
- `src/views/todo-sidebar-view.ts` — Main sidebar panel (`TodoSidebarView` extends `ItemView`)
- `src/views/todo-item-component.ts` — Pure DOM factory for individual todo `<li>` elements
- `src/core/todo-parser.ts` — Parses markdown into `TodoGroup[]` with nested `TodoItem` trees
- `src/core/todo-transformer.ts` — Pure functions for modifying file content (toggle, move, indent)
- `src/modals/note-select-modal.ts` — First step of cross-file move: pick target note
- `src/modals/heading-select-modal.ts` — Second step: pick heading within target note
### Data Flow
1. `parseTodosGroupedByHeading(content)` → `TodoGroup[]` (each group has a heading + tree of todos)
2. `buildTodoTree(flatTodos)` — stack-based algorithm: todos whose indent > parent's become children
3. `renderGroups()` renders each group; `createTodoItemEl()` renders items recursively
4. `flatTodoList: FlatTodoEntry[]` is rebuilt each render for keyboard navigation
### Event Listener Lifecycle (Critical)
- **Register keydown listener ONCE in `onOpen()`**, not in `renderGroups()` — otherwise listeners accumulate on every file change and arrow keys skip items
- Use `this.currentFile` inside handlers instead of closure variables (file changes between renders)
- `registerEvent()` wrappers handle cleanup automatically on view close
### Keyboard Navigation
- `focusedIndex = -1` means panel is not yet activated (no visual focus)
- First ArrowDown/ArrowUp when `focusedIndex === -1` sets it to 0 (activates focus like Outline view)
- `flatTodoList` flattens the entire tree depth-first; arrow keys walk this list linearly
- Container has `tabindex="0"` so it can receive keyboard events without stealing focus on render
### Drag and Drop
- Parent `<li>` and all nested `<li>` are `draggable="true"`; `stopPropagation` on `dragstart` prevents child drag from bubbling to parent
- `handleDragStart` stores `draggedTodo` and `draggedChildLines` (via `collectChildLineNumbers`)
- Item drop zones use 3 zones (top/middle/bottom thirds):
- Top → insert above
- Middle → nest as child (increase indent, insert after target's last descendant)
- Bottom → insert below
- Group (heading) drop zones allow dropping onto the heading area to move to end of that section
- `performNest` adjusts indentation with `indentTodoLines(lines, delta)` before inserting
### Hotkey Matching (Platform-Aware)
- Uses `app.hotkeyManager.getHotkeys(commandId)` and `getDefaultHotkeys(commandId)` (internal API)
- `Platform.isMacOS` distinguishes Mod (Cmd on Mac, Ctrl on Windows) from Ctrl
- See `matchesHotkey()` in `todo-sidebar-view.ts` for the full matching logic
### Cross-File Move
- Always pass `addBlankLine: true` to `insertTodoUnderHeading` from modals — inserts a blank line between the heading and the moved todo for readability
- In-file drag moves use `moveTodoWithChildren` directly (no blank line needed)