commit 96699a90d3ad32dbfd771ca5e4b83124baba8656
parent aee6fcadf42a45c8e4e2447d9e73ff8391ac2867
Author: Hunter
Date:   Thu,  1 Aug 2024 01:03:10 -0400

refined down arrow stopping

Diffstat:
Mindex.html | 1062++++++++++++++++++++++++++++++++++++++++---------------------------------------
1 file changed, 536 insertions(+), 526 deletions(-)

diff --git a/index.html b/index.html @@ -1,534 +1,544 @@ <!DOCTYPE html> <html lang="en"> <head> - <meta charset="UTF-8"> - <meta name="viewport" content="width=device-width, initial-scale=1.0"> - <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🪆</text></svg>"> - <title>todo</title> - <style> - body { - font-family: Arial, sans-serif; - max-width: 800px; - margin: 0 auto; - padding: 20px; - } - ul { - list-style-type: none; - padding-left: 20px; - } - input[type="text"] { - border: none; - background: transparent; - font-size: 1.17em; - font-weight: bold; - width: calc(100% - 30px); - margin-left: 5px; - padding: 5px; - } - input[type="text"]:focus { - outline: none; - } - .task-container { - display: flex; - align-items: center; - margin: 5px 0; - } - .active { - background-color: #e6f3ff; - } - .parent-task { - font-size: 1.5em; - font-weight: bold; - } - #breadcrumbs { - font-size: 24px; - margin-bottom: 10px; - } - </style> - <script> - document.addEventListener('DOMContentLoaded', function() { - const appContainer = document.getElementById('app-container'); - let currentTask = { id: 'root', text: 'todo', state: 0, subtasks: [{ id: Date.now(), text: '', state: 0, subtasks: [] }], selectedSubtaskId: null }; - let taskPath = [currentTask]; - - const keyHandler = { - backspace: { - canDelete: true, - blocked: false - }, - enter: { - canAdd: true, - blocked: false - }, - arrowDown: { - canAdd: true, - blocked: false - } - }; - - function createTaskElement(task, isParentTask = false) { - const taskContainer = document.createElement('div'); - taskContainer.className = 'task-container'; - taskContainer.dataset.id = task.id; - if (isParentTask) taskContainer.classList.add('parent-task'); - - const checkbox = document.createElement('input'); - checkbox.type = 'checkbox'; - updateCheckboxState(checkbox, task.state); - checkbox.addEventListener('click', (e) => { - e.preventDefault(); - toggleTaskState(task); - }); - - const taskInput = document.createElement('input'); - taskInput.type = 'text'; - taskInput.value = task.text; - - const keydownHandler = function(e) { - if (e.key === 'Backspace') { - if (keyHandler.backspace.blocked) { - e.preventDefault(); - return; - } - if (taskInput.value === '' && keyHandler.backspace.canDelete) { - e.preventDefault(); - if (task !== taskPath[0]) { - keyHandler.backspace.blocked = true; - if (task === currentTask) { - deleteCurrentParentTask(); - } else { - deleteSubtask(task); - } - } - } else if (taskInput.value !== '') { - keyHandler.backspace.canDelete = false; - } - } else if (e.key === 'Enter' && !e.shiftKey) { - if (keyHandler.enter.blocked) { - e.preventDefault(); - return; - } - if (keyHandler.enter.canAdd) { - e.preventDefault(); - keyHandler.enter.blocked = true; - addNewSubtask(currentTask, task); - } - } else if (e.key === 'ArrowDown' && !e.shiftKey) { - if (keyHandler.arrowDown.blocked) { - e.preventDefault(); - return; - } - if (keyHandler.arrowDown.canAdd && isLastSubtask(task)) { - e.preventDefault(); - keyHandler.arrowDown.blocked = true; - addNewSubtask(currentTask, task); - } else { - handleKeyDown(e, task); - } - } else { - handleKeyDown(e, task); - } - }; - - const keyupHandler = function(e) { - if (e.key === 'Backspace') { - keyHandler.backspace.canDelete = true; - keyHandler.backspace.blocked = false; - } else if (e.key === 'Enter') { - keyHandler.enter.canAdd = true; - keyHandler.enter.blocked = false; - } else if (e.key === 'ArrowDown') { - keyHandler.arrowDown.canAdd = true; - keyHandler.arrowDown.blocked = false; - } - }; - - taskInput.addEventListener('keydown', keydownHandler); - taskInput.addEventListener('keyup', keyupHandler); - taskInput.addEventListener('keydown', copyActiveTaskText); - - taskInput.addEventListener('input', () => { - task.text = taskInput.value; - if (task === currentTask) { - updatePageTitle(task); - } - }); - - taskInput.addEventListener('focus', () => setActiveTask(taskInput, task)); - - taskContainer.appendChild(checkbox); - taskContainer.appendChild(taskInput); - - return taskContainer; - } - - function generateBreadcrumbs(rootTask, currentPath, selectedTaskId) { - let breadcrumbs = ''; - let currentTask = rootTask; - let currentDepth = 0; - - for (let i = 0; i < currentPath.length - 1; i++) { - breadcrumbs += '○ '; - currentTask = currentTask.subtasks.find(t => t.id === currentPath[i + 1].id); - } - - breadcrumbs += '● '; - currentDepth = currentPath.length - 1; - - if (selectedTaskId !== currentTask.id) { - let selectedTask = currentTask.subtasks.find(t => t.id === selectedTaskId); - - if (selectedTask) { - function calculateMaxDepth(task, depth) { - if (task.subtasks.length === 0) return depth; - return Math.max(...task.subtasks.map(st => calculateMaxDepth(st, depth + 1))); - } - - let maxDepth = calculateMaxDepth(selectedTask, currentDepth + 1); - - for (let i = currentDepth + 1; i < maxDepth; i++) { - breadcrumbs += '○ '; - } - } - } - - return breadcrumbs.trim(); - } - - function updateCheckboxState(checkbox, state) { - checkbox.checked = state === 1; - checkbox.indeterminate = state === 2; - } - - function toggleTaskState(task) { - if (task.state === 1) { - if (task.subtasks.length === 0 || !task.subtasks.every(t => t.state === 1)) { - task.state = 0; - if (task.subtasks.length > 0) { - task.subtasks.forEach(subtask => { - if (subtask.state === 2) { - subtask.state = 0; - } - }); - } - } - } else { - task.state = 1; - if (task.subtasks.length > 0) { - task.subtasks.forEach(subtask => { - if (subtask.state === 0) { - subtask.state = 2; - } - }); - } - } - updateParentTaskState(task); - renderCurrentView(); - selectAndFocusTask(task); - } - - function updateParentTaskState(task) { - const parent = findParentTask(task); - if (parent) { - const allChecked = parent.subtasks.every(t => t.state === 1); - const anyUnchecked = parent.subtasks.some(t => t.state === 0); - - if (allChecked) { - parent.state = 1; - } else if (anyUnchecked) { - parent.state = 0; - } - - updateParentTaskState(parent); - } - } - - function updateSubtasksState(task, state) { - task.subtasks.forEach(subtask => { - subtask.state = state; - if (subtask.subtasks.length > 0) { - updateSubtasksState(subtask, state); - } - }); - } - - function findParentTask(task) { - for (let i = taskPath.length - 1; i >= 0; i--) { - const potentialParent = taskPath[i]; - if (potentialParent.subtasks.some(t => t.id === task.id)) { - return potentialParent; - } - } - return null; - } - - function handleKeyDown(e, task) { - if (e.key === 'Enter' && e.shiftKey) { - e.preventDefault(); - toggleTaskState(task); - } else if (e.key === 'ArrowUp' && !e.shiftKey) { - e.preventDefault(); - navigateTasks('up'); - } else if (e.key === 'ArrowDown' && !e.shiftKey) { - e.preventDefault(); - navigateTasks('down'); - } else if (e.key === 'ArrowUp' && e.shiftKey) { - e.preventDefault(); - moveSubtask(task, 'up'); - } else if (e.key === 'ArrowDown' && e.shiftKey) { - e.preventDefault(); - moveSubtask(task, 'down'); - } else if (e.key === 'ArrowRight' && e.shiftKey) { - e.preventDefault(); - if (task !== currentTask) { - navigateIntoSubtask(task); - } - } else if (e.key === 'ArrowLeft' && e.shiftKey) { - e.preventDefault(); - navigateToParentTask(); - } - } - - function moveSubtask(subtask, direction) { - const parentTask = findParentTask(subtask); - if (!parentTask) return; - - const index = parentTask.subtasks.findIndex(t => t.id === subtask.id); - if (index === -1) return; - - if (direction === 'up' && index > 0) { - [parentTask.subtasks[index - 1], parentTask.subtasks[index]] = [parentTask.subtasks[index], parentTask.subtasks[index - 1]]; - } else if (direction === 'down' && index < parentTask.subtasks.length - 1) { - [parentTask.subtasks[index], parentTask.subtasks[index + 1]] = [parentTask.subtasks[index + 1], parentTask.subtasks[index]]; - } - - renderCurrentView(); - selectAndFocusTask(subtask); - } - - function addNewSubtask(parentTask, currentSubtask = null) { - const newSubtask = { id: Date.now(), text: '', state: 0, subtasks: [], selectedSubtaskId: null }; - if (currentSubtask) { - const index = parentTask.subtasks.findIndex(t => t.id === currentSubtask.id); - parentTask.subtasks.splice(index + 1, 0, newSubtask); - } else { - parentTask.subtasks.push(newSubtask); - } - - parentTask.state = 0; - parentTask.subtasks.forEach(subtask => { - if (subtask.state === 2) { - subtask.state = 0; - } - }); - - renderCurrentView(); - selectAndFocusTask(newSubtask); - } - - function selectAndFocusTask(task) { - const taskInput = document.querySelector(`.task-container[data-id="${task.id}"] input[type="text"]`); - if (taskInput) { - taskInput.focus(); - setActiveTask(taskInput, task); - } - } - - function deleteSubtask(subtask) { - const parentTask = taskPath[taskPath.length - 1]; - const index = parentTask.subtasks.findIndex(t => t.id === subtask.id); - - if (parentTask.id === 'root' && parentTask.subtasks.length === 1) { - return; - } - - parentTask.subtasks = parentTask.subtasks.filter(t => t.id !== subtask.id); - updateParentTaskState(parentTask); - - if (parentTask.subtasks.length === 0 && taskPath.length > 1) { - navigateToParentTask(); - } else { - renderCurrentView(); - if (parentTask.subtasks.length > 0) { - const targetIndex = Math.max(0, index - 1); - selectAndFocusTask(parentTask.subtasks[targetIndex]); - } else { - selectAndFocusTask(parentTask); - } - } - } - - function deleteCurrentParentTask() { - if (taskPath.length <= 1) return; // Don't delete root task - - const currentParentTask = taskPath[taskPath.length - 1]; - const grandparentTask = taskPath[taskPath.length - 2]; - - // Check if we're trying to delete the sole child of the root - if (grandparentTask.id === 'root' && grandparentTask.subtasks.length === 1) { - return; // Don't allow deletion of the sole child of root - } - - const index = grandparentTask.subtasks.findIndex(t => t.id === currentParentTask.id); - - grandparentTask.subtasks = grandparentTask.subtasks.filter(t => t.id !== currentParentTask.id); - updateParentTaskState(grandparentTask); - - taskPath.pop(); // Remove the deleted task from the path - currentTask = grandparentTask; - - if (grandparentTask.subtasks.length === 0 && taskPath.length > 1) { - // If we've just deleted the last subtask, navigate up another level - navigateToParentTask(); - } else { - renderCurrentView(); - if (grandparentTask.subtasks.length > 0) { - // Select the subtask before the deleted one, or the one after if at the start - const targetIndex = Math.max(0, index - 1); - selectAndFocusTask(grandparentTask.subtasks[targetIndex]); - } else { - selectAndFocusTask(grandparentTask); - } - } - } - - function navigateTasks(direction) { - const tasks = currentTask.subtasks; - const currentElement = document.activeElement; - const currentContainer = currentElement.closest('.task-container'); - const currentIndex = Array.from(appContainer.querySelectorAll('.task-container')).indexOf(currentContainer); - - if (direction === 'up') { - if (currentIndex > 0) { - const prevTask = currentIndex === 1 ? currentTask : tasks[currentIndex - 2]; - selectAndFocusTask(prevTask); - } - } else { - if (currentIndex < tasks.length) { - selectAndFocusTask(tasks[currentIndex]); - } - } - } - - function navigateIntoSubtask(subtask) { - if (subtask.subtasks.length > 0) { - currentTask.selectedSubtaskId = subtask.id; - taskPath.push(subtask); + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🪆</text></svg>"> + <title>todo</title> + <style> + body { + font-family: Arial, sans-serif; + max-width: 800px; + margin: 0 auto; + padding: 20px; + } + ul { + list-style-type: none; + padding-left: 20px; + } + input[type="text"] { + border: none; + background: transparent; + font-size: 1.17em; + font-weight: bold; + width: calc(100% - 30px); + margin-left: 5px; + padding: 5px; + } + input[type="text"]:focus { + outline: none; + } + .task-container { + display: flex; + align-items: center; + margin: 5px 0; + } + .active { + background-color: #e6f3ff; + } + .parent-task { + font-size: 1.5em; + font-weight: bold; + } + #breadcrumbs { + font-size: 24px; + margin-bottom: 10px; + } + </style> + <script> + document.addEventListener('DOMContentLoaded', function() { + const appContainer = document.getElementById('app-container'); + let currentTask = { id: 'root', text: 'todo', state: 0, subtasks: [{ id: Date.now(), text: '', state: 0, subtasks: [] }], selectedSubtaskId: null }; + let taskPath = [currentTask]; + let lastSubtaskDownArrowReleased = false; + + const keyHandler = { + backspace: { + canDelete: true, + blocked: false + }, + enter: { + canAdd: true, + blocked: false + }, + arrowDown: { + canAdd: true, + blocked: false + } + }; + + function createTaskElement(task, isParentTask = false) { + const taskContainer = document.createElement('div'); + taskContainer.className = 'task-container'; + taskContainer.dataset.id = task.id; + if (isParentTask) taskContainer.classList.add('parent-task'); + + const checkbox = document.createElement('input'); + checkbox.type = 'checkbox'; + updateCheckboxState(checkbox, task.state); + checkbox.addEventListener('click', (e) => { + e.preventDefault(); + toggleTaskState(task); + }); + + const taskInput = document.createElement('input'); + taskInput.type = 'text'; + taskInput.value = task.text; + + const keydownHandler = function(e) { + if (e.key === 'Backspace') { + if (keyHandler.backspace.blocked) { + e.preventDefault(); + return; + } + if (taskInput.value === '' && keyHandler.backspace.canDelete) { + e.preventDefault(); + if (task !== taskPath[0]) { + keyHandler.backspace.blocked = true; + if (task === currentTask) { + deleteCurrentParentTask(); + } else { + deleteSubtask(task); + } + } + } else if (taskInput.value !== '') { + keyHandler.backspace.canDelete = false; + } + } else if (e.key === 'Enter' && !e.shiftKey) { + if (keyHandler.enter.blocked) { + e.preventDefault(); + return; + } + if (keyHandler.enter.canAdd) { + e.preventDefault(); + keyHandler.enter.blocked = true; + addNewSubtask(currentTask, task); + } + } else if (e.key === 'ArrowDown' && !e.shiftKey) { + if (keyHandler.arrowDown.blocked) { + e.preventDefault(); + return; + } + if (isLastSubtask(task) && (lastSubtaskDownArrowReleased || currentTask.subtasks.length === 1)) { + e.preventDefault(); + keyHandler.arrowDown.blocked = true; + addNewSubtask(currentTask, task); + lastSubtaskDownArrowReleased = false; + } else { + handleKeyDown(e, task); + } + } else { + handleKeyDown(e, task); + } + }; + + const keyupHandler = function(e) { + if (e.key === 'Backspace') { + keyHandler.backspace.canDelete = true; + keyHandler.backspace.blocked = false; + } else if (e.key === 'Enter') { + keyHandler.enter.canAdd = true; + keyHandler.enter.blocked = false; + } else if (e.key === 'ArrowDown') { + keyHandler.arrowDown.canAdd = true; + keyHandler.arrowDown.blocked = false; + if (isLastSubtask(task)) { + lastSubtaskDownArrowReleased = true; + } else { + lastSubtaskDownArrowReleased = false; + } + } + }; + + taskInput.addEventListener('keydown', keydownHandler); + taskInput.addEventListener('keyup', keyupHandler); + taskInput.addEventListener('keydown', copyActiveTaskText); + + taskInput.addEventListener('input', () => { + task.text = taskInput.value; + if (task === currentTask) { + updatePageTitle(task); + } + }); + + taskInput.addEventListener('focus', () => setActiveTask(taskInput, task)); + + taskContainer.appendChild(checkbox); + taskContainer.appendChild(taskInput); + + return taskContainer; + } + + function generateBreadcrumbs(rootTask, currentPath, selectedTaskId) { + let breadcrumbs = ''; + let currentTask = rootTask; + let currentDepth = 0; + + for (let i = 0; i < currentPath.length - 1; i++) { + breadcrumbs += '○ '; + currentTask = currentTask.subtasks.find(t => t.id === currentPath[i + 1].id); + } + + breadcrumbs += '● '; + currentDepth = currentPath.length - 1; + + if (selectedTaskId !== currentTask.id) { + let selectedTask = currentTask.subtasks.find(t => t.id === selectedTaskId); + + if (selectedTask) { + function calculateMaxDepth(task, depth) { + if (task.subtasks.length === 0) return depth; + return Math.max(...task.subtasks.map(st => calculateMaxDepth(st, depth + 1))); + } + + let maxDepth = calculateMaxDepth(selectedTask, currentDepth + 1); + + for (let i = currentDepth + 1; i < maxDepth; i++) { + breadcrumbs += '○ '; + } + } + } + + return breadcrumbs.trim(); + } + + function updateCheckboxState(checkbox, state) { + checkbox.checked = state === 1; + checkbox.indeterminate = state === 2; + } + + function toggleTaskState(task) { + if (task.state === 1) { + if (task.subtasks.length === 0 || !task.subtasks.every(t => t.state === 1)) { + task.state = 0; + if (task.subtasks.length > 0) { + task.subtasks.forEach(subtask => { + if (subtask.state === 2) { + subtask.state = 0; + } + }); + } + } + } else { + task.state = 1; + if (task.subtasks.length > 0) { + task.subtasks.forEach(subtask => { + if (subtask.state === 0) { + subtask.state = 2; + } + }); + } + } + updateParentTaskState(task); + renderCurrentView(); + selectAndFocusTask(task); + } + + function updateParentTaskState(task) { + const parent = findParentTask(task); + if (parent) { + const allChecked = parent.subtasks.every(t => t.state === 1); + const anyUnchecked = parent.subtasks.some(t => t.state === 0); + + if (allChecked) { + parent.state = 1; + } else if (anyUnchecked) { + parent.state = 0; + } + + updateParentTaskState(parent); + } + } + + function updateSubtasksState(task, state) { + task.subtasks.forEach(subtask => { + subtask.state = state; + if (subtask.subtasks.length > 0) { + updateSubtasksState(subtask, state); + } + }); + } + + function findParentTask(task) { + for (let i = taskPath.length - 1; i >= 0; i--) { + const potentialParent = taskPath[i]; + if (potentialParent.subtasks.some(t => t.id === task.id)) { + return potentialParent; + } + } + return null; + } + + function handleKeyDown(e, task) { + if (e.key === 'Enter' && e.shiftKey) { + e.preventDefault(); + toggleTaskState(task); + } else if (e.key === 'ArrowUp' && !e.shiftKey) { + e.preventDefault(); + navigateTasks('up'); + } else if (e.key === 'ArrowDown' && !e.shiftKey) { + e.preventDefault(); + navigateTasks('down'); + } else if (e.key === 'ArrowUp' && e.shiftKey) { + e.preventDefault(); + moveSubtask(task, 'up'); + } else if (e.key === 'ArrowDown' && e.shiftKey) { + e.preventDefault(); + moveSubtask(task, 'down'); + } else if (e.key === 'ArrowRight' && e.shiftKey) { + e.preventDefault(); + if (task !== currentTask) { + navigateIntoSubtask(task); + } + } else if (e.key === 'ArrowLeft' && e.shiftKey) { + e.preventDefault(); + navigateToParentTask(); + } + } + + function moveSubtask(subtask, direction) { + const parentTask = findParentTask(subtask); + if (!parentTask) return; + + const index = parentTask.subtasks.findIndex(t => t.id === subtask.id); + if (index === -1) return; + + if (direction === 'up' && index > 0) { + [parentTask.subtasks[index - 1], parentTask.subtasks[index]] = [parentTask.subtasks[index], parentTask.subtasks[index - 1]]; + } else if (direction === 'down' && index < parentTask.subtasks.length - 1) { + [parentTask.subtasks[index], parentTask.subtasks[index + 1]] = [parentTask.subtasks[index + 1], parentTask.subtasks[index]]; + } + + renderCurrentView(); + selectAndFocusTask(subtask); + } + + function addNewSubtask(parentTask, currentSubtask = null) { + const newSubtask = { id: Date.now(), text: '', state: 0, subtasks: [], selectedSubtaskId: null }; + if (currentSubtask) { + const index = parentTask.subtasks.findIndex(t => t.id === currentSubtask.id); + parentTask.subtasks.splice(index + 1, 0, newSubtask); + } else { + parentTask.subtasks.push(newSubtask); + } + + parentTask.state = 0; + parentTask.subtasks.forEach(subtask => { + if (subtask.state === 2) { + subtask.state = 0; + } + }); + + renderCurrentView(); + selectAndFocusTask(newSubtask); + } + + function selectAndFocusTask(task) { + const taskInput = document.querySelector(`.task-container[data-id="${task.id}"] input[type="text"]`); + if (taskInput) { + taskInput.focus(); + setActiveTask(taskInput, task); + } + } + + function deleteSubtask(subtask) { + const parentTask = taskPath[taskPath.length - 1]; + const index = parentTask.subtasks.findIndex(t => t.id === subtask.id); + + if (parentTask.id === 'root' && parentTask.subtasks.length === 1) { + return; + } + + parentTask.subtasks = parentTask.subtasks.filter(t => t.id !== subtask.id); + updateParentTaskState(parentTask); + + if (parentTask.subtasks.length === 0 && taskPath.length > 1) { + navigateToParentTask(); + } else { + renderCurrentView(); + if (parentTask.subtasks.length > 0) { + const targetIndex = Math.max(0, index - 1); + selectAndFocusTask(parentTask.subtasks[targetIndex]); + } else { + selectAndFocusTask(parentTask); + } + } + } + + function deleteCurrentParentTask() { + if (taskPath.length <= 1) return; // Don't delete root task + + const currentParentTask = taskPath[taskPath.length - 1]; + const grandparentTask = taskPath[taskPath.length - 2]; + + // Check if we're trying to delete the sole child of the root + if (grandparentTask.id === 'root' && grandparentTask.subtasks.length === 1) { + return; // Don't allow deletion of the sole child of root + } + + const index = grandparentTask.subtasks.findIndex(t => t.id === currentParentTask.id); + + grandparentTask.subtasks = grandparentTask.subtasks.filter(t => t.id !== currentParentTask.id); + updateParentTaskState(grandparentTask); + + taskPath.pop(); // Remove the deleted task from the path + currentTask = grandparentTask; + + if (grandparentTask.subtasks.length === 0 && taskPath.length > 1) { + // If we've just deleted the last subtask, navigate up another level + navigateToParentTask(); + } else { + renderCurrentView(); + if (grandparentTask.subtasks.length > 0) { + // Select the subtask before the deleted one, or the one after if at the start + const targetIndex = Math.max(0, index - 1); + selectAndFocusTask(grandparentTask.subtasks[targetIndex]); + } else { + selectAndFocusTask(grandparentTask); + } + } + } + + function navigateTasks(direction) { + const tasks = currentTask.subtasks; + const currentElement = document.activeElement; + const currentContainer = currentElement.closest('.task-container'); + const currentIndex = Array.from(appContainer.querySelectorAll('.task-container')).indexOf(currentContainer); + + if (direction === 'up') { + if (currentIndex > 0) { + const prevTask = currentIndex === 1 ? currentTask : tasks[currentIndex - 2]; + selectAndFocusTask(prevTask); + } + // } else if (currentTask !== taskPath[0]) { + // navigateToParentTask(); + // } + } else { + if (currentIndex < tasks.length) { + selectAndFocusTask(tasks[currentIndex]); + } + } + lastSubtaskDownArrowReleased = false; + } + + function navigateIntoSubtask(subtask) { + if (subtask.subtasks.length > 0) { + currentTask.selectedSubtaskId = subtask.id; + taskPath.push(subtask); currentTask = subtask; - updateBreadcrumbs(currentTask); - renderCurrentView(); - const selectedSubtask = subtask.selectedSubtaskId ? - subtask.subtasks.find(t => t.id === subtask.selectedSubtaskId) : - subtask.subtasks[0]; - selectAndFocusTask(selectedSubtask); - } else { - addNewSubtask(subtask); - currentTask.selectedSubtaskId = subtask.id; - taskPath.push(subtask); - currentTask = subtask; - updateBreadcrumbs(currentTask); - renderCurrentView(); - selectAndFocusTask(subtask.subtasks[0]); - } - } - - function navigateToParentTask() { - if (taskPath.length > 1) { - const currentTaskId = currentTask.id; - taskPath.pop(); - currentTask = taskPath[taskPath.length - 1]; - updateParentTaskState(currentTask); - updateBreadcrumbs(currentTask); - renderCurrentView(); - const selectedSubtask = currentTask.subtasks.find(t => t.id === currentTaskId); - if (selectedSubtask) { - selectAndFocusTask(selectedSubtask); - } else if (currentTask.subtasks.length > 0) { - // If for some reason the previous subtask isn't found, select the first subtask - selectAndFocusTask(currentTask.subtasks[0]); - } else { - selectAndFocusTask(currentTask); - } - currentTask.selectedSubtaskId = currentTaskId; - } - } - - function setActiveTask(input, task) { - document.querySelectorAll('.active').forEach(el => el.classList.remove('active')); - input.closest('.task-container').classList.add('active'); - if (task !== currentTask) { - currentTask.selectedSubtaskId = task.id; - } - updateBreadcrumbs(task); - } - - function updateBreadcrumbs(selectedTask) { - const breadcrumbsContainer = document.getElementById('breadcrumbs'); - const trail = generateBreadcrumbs(taskPath[0], taskPath, selectedTask.id); - breadcrumbsContainer.textContent = trail; - } - - function renderCurrentView() { - appContainer.innerHTML = ''; - currentTask = taskPath[taskPath.length - 1]; - - updateBreadcrumbs(currentTask); - updatePageTitle(currentTask); - - const parentElement = createTaskElement(currentTask, true); - appContainer.appendChild(parentElement); - - const subtasksList = document.createElement('ul'); - currentTask.subtasks.forEach(subtask => { - const li = document.createElement('li'); - li.appendChild(createTaskElement(subtask)); - subtasksList.appendChild(li); - }); - appContainer.appendChild(subtasksList); - - const parentCheckbox = parentElement.querySelector('input[type="checkbox"]'); - updateCheckboxState(parentCheckbox, currentTask.state); - - if (currentTask.selectedSubtaskId) { - const selectedTask = currentTask.subtasks.find(t => t.id === currentTask.selectedSubtaskId); - if (selectedTask) { - selectAndFocusTask(selectedTask); - } - } - } - - function copyActiveTaskText(e) { - // Check if the key combination is Ctrl+C (Windows) or Cmd+C (Mac) - if ((e.ctrlKey || e.metaKey) && e.key === 'c') { - const activeTaskInput = document.querySelector('.task-container.active input[type="text"]'); - if (activeTaskInput) { - // Check if there's a selection within the active task input - const selectedText = activeTaskInput.value.substring(activeTaskInput.selectionStart, activeTaskInput.selectionEnd); - - const textToCopy = selectedText || activeTaskInput.value; - - e.preventDefault(); // Prevent default copy behavior - navigator.clipboard.writeText(textToCopy); - } - } - } - - function updatePageTitle(task) { - document.title = task.text || '?'; - } - - function isLastSubtask(task) { - const parentTask = findParentTask(task); - if (!parentTask) return false; - return parentTask.subtasks[parentTask.subtasks.length - 1].id === task.id; - } - - renderCurrentView(); - }); - </script> + updateBreadcrumbs(currentTask); + renderCurrentView(); + const selectedSubtask = subtask.selectedSubtaskId ? + subtask.subtasks.find(t => t.id === subtask.selectedSubtaskId) : + subtask.subtasks[0]; + selectAndFocusTask(selectedSubtask); + } else { + addNewSubtask(subtask); + currentTask.selectedSubtaskId = subtask.id; + taskPath.push(subtask); + currentTask = subtask; + updateBreadcrumbs(currentTask); + renderCurrentView(); + selectAndFocusTask(subtask.subtasks[0]); + } + } + + function navigateToParentTask() { + if (taskPath.length > 1) { + const currentTaskId = currentTask.id; + taskPath.pop(); + currentTask = taskPath[taskPath.length - 1]; + updateParentTaskState(currentTask); + updateBreadcrumbs(currentTask); + renderCurrentView(); + const selectedSubtask = currentTask.subtasks.find(t => t.id === currentTaskId); + if (selectedSubtask) { + selectAndFocusTask(selectedSubtask); + } else if (currentTask.subtasks.length > 0) { + selectAndFocusTask(currentTask.subtasks[0]); + } else { + selectAndFocusTask(currentTask); + } + currentTask.selectedSubtaskId = currentTaskId; + } + } + + function setActiveTask(input, task) { + document.querySelectorAll('.active').forEach(el => el.classList.remove('active')); + input.closest('.task-container').classList.add('active'); + if (task !== currentTask) { + currentTask.selectedSubtaskId = task.id; + } + updateBreadcrumbs(task); + } + + function updateBreadcrumbs(selectedTask) { + const breadcrumbsContainer = document.getElementById('breadcrumbs'); + const trail = generateBreadcrumbs(taskPath[0], taskPath, selectedTask.id); + breadcrumbsContainer.textContent = trail; + } + + function renderCurrentView() { + appContainer.innerHTML = ''; + currentTask = taskPath[taskPath.length - 1]; + + updateBreadcrumbs(currentTask); + updatePageTitle(currentTask); + + const parentElement = createTaskElement(currentTask, true); + appContainer.appendChild(parentElement); + + const subtasksList = document.createElement('ul'); + currentTask.subtasks.forEach(subtask => { + const li = document.createElement('li'); + li.appendChild(createTaskElement(subtask)); + subtasksList.appendChild(li); + }); + appContainer.appendChild(subtasksList); + + const parentCheckbox = parentElement.querySelector('input[type="checkbox"]'); + updateCheckboxState(parentCheckbox, currentTask.state); + + if (currentTask.selectedSubtaskId) { + const selectedTask = currentTask.subtasks.find(t => t.id === currentTask.selectedSubtaskId); + if (selectedTask) { + selectAndFocusTask(selectedTask); + } + } + } + + function copyActiveTaskText(e) { + // Check if the key combination is Ctrl+C (Windows) or Cmd+C (Mac) + if ((e.ctrlKey || e.metaKey) && e.key === 'c') { + const activeTaskInput = document.querySelector('.task-container.active input[type="text"]'); + if (activeTaskInput) { + // Check if there's a selection within the active task input + const selectedText = activeTaskInput.value.substring(activeTaskInput.selectionStart, activeTaskInput.selectionEnd); + + const textToCopy = selectedText || activeTaskInput.value; + + e.preventDefault(); // Prevent default copy behavior + navigator.clipboard.writeText(textToCopy); + } + } + } + + function updatePageTitle(task) { + document.title = task.text || '?'; + } + + function isLastSubtask(task) { + const parentTask = findParentTask(task); + if (!parentTask) return false; + return parentTask.subtasks[parentTask.subtasks.length - 1].id === task.id; + } + + renderCurrentView(); + }); + </script> </head> <body> - <div id="breadcrumbs"></div> - <div id="app-container"></div> + <div id="breadcrumbs"></div> + <div id="app-container"></div> </body> </html> \ No newline at end of file