Add focus activation, 3-zone drag nesting, and click-to-navigate
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:
@@ -137,18 +137,47 @@ export function moveTodoUnderHeadingInFile(
|
||||
return moveTodoWithChildren(content, todoLineNumber, childLineNumbers, targetLineNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust indentation of todo lines by a delta (positive = indent, negative = outdent).
|
||||
* Each level is 2 spaces. Will not outdent past zero.
|
||||
*/
|
||||
export function indentTodoLines(todoLines: string[], indentDelta: number): string[] {
|
||||
if (indentDelta === 0) return [...todoLines];
|
||||
|
||||
const indent = ' '; // 2 spaces per level
|
||||
|
||||
return todoLines.map((line) => {
|
||||
if (indentDelta > 0) {
|
||||
return indent.repeat(indentDelta) + line;
|
||||
} else {
|
||||
// Outdent: remove leading spaces
|
||||
let result = line;
|
||||
for (let i = 0; i < Math.abs(indentDelta); i++) {
|
||||
if (result.startsWith(indent)) {
|
||||
result = result.slice(indent.length);
|
||||
} else if (result.startsWith('\t')) {
|
||||
result = result.slice(1);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a todo line under a specific heading.
|
||||
* @param content - The file content
|
||||
* @param headingLineNumber - The line number of the heading (0-based)
|
||||
* @param todoLine - The todo line to insert
|
||||
* @param nextHeadingLineNumber - Optional line number of the next heading (for section boundary)
|
||||
* @param addBlankLine - Whether to add a blank line between the heading and the todo
|
||||
*/
|
||||
export function insertTodoUnderHeading(
|
||||
content: string,
|
||||
headingLineNumber: number,
|
||||
todoLine: string,
|
||||
nextHeadingLineNumber?: number
|
||||
nextHeadingLineNumber?: number,
|
||||
addBlankLine = false
|
||||
): string {
|
||||
const lines = content.split('\n');
|
||||
|
||||
@@ -156,15 +185,26 @@ export function insertTodoUnderHeading(
|
||||
let insertPosition: number;
|
||||
|
||||
if (nextHeadingLineNumber !== undefined) {
|
||||
// Insert just before the next heading (at the end of this section)
|
||||
insertPosition = nextHeadingLineNumber;
|
||||
} else {
|
||||
// No next heading - insert right after the heading line
|
||||
insertPosition = headingLineNumber + 1;
|
||||
}
|
||||
|
||||
// Insert the todo line
|
||||
lines.splice(insertPosition, 0, todoLine);
|
||||
if (addBlankLine) {
|
||||
// Check if there's already a blank line at the insert position
|
||||
const lineAfterHeading = lines[headingLineNumber + 1];
|
||||
const alreadyHasBlank = lineAfterHeading !== undefined && lineAfterHeading.trim() === '';
|
||||
|
||||
if (alreadyHasBlank) {
|
||||
// Insert after the existing blank line
|
||||
lines.splice(headingLineNumber + 2, 0, todoLine);
|
||||
} else {
|
||||
// Insert blank line + todo
|
||||
lines.splice(insertPosition, 0, '', todoLine);
|
||||
}
|
||||
} else {
|
||||
lines.splice(insertPosition, 0, todoLine);
|
||||
}
|
||||
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user