Merge pull request #1 from hunterirving/scrolling-enhancements
Scrolling enhancements
Hunter Irving
Tuesday, April 1st, 2025 at 12:12 AM
-CLAUDE.md
1 +CLAUDE.md
261 }
262 });
263 document.addEventListener('DOMContentLoaded', function() {
264 + // Variables for scroll handling
265 + let lastScrollPosition = 0;
266 + let isScrolling = false;
267 + let scrollDebounceTimer;
268 const appContainer = document.getElementById('app-container');
269 let rootTask = loadTasksFromLocalStorage();
270 let currentTask = rootTask;
916 updateBreadcrumbs(task);
917 lastSubtaskDownArrowReleased = isLastSubtask(task);
918 input.focus();
919 +
920 + // Center the active task in the viewport
921 + setTimeout(() => {
922 + const activeTaskElement = document.querySelector('.task-container.active');
923 + if (activeTaskElement && activeTaskElement.parentElement && activeTaskElement.parentElement.tagName === 'LI') {
924 + // Use scrollIntoView with block: "center" to center the element vertically
925 + activeTaskElement.scrollIntoView({
926 + behavior: 'auto', // Use 'auto' for immediate scrolling
927 + block: 'center', // Center vertically
928 + inline: 'nearest' // Don't change horizontal scroll
929 + });
930 + }
931 + }, 10);
932 }
933
934 function updateBreadcrumbs(selectedTask) {
1097
1098 document.addEventListener('keydown', handleSave);
1099 document.addEventListener('keydown', handleOpen);
1100 +
1101 + // Add scroll handling to navigate tasks with inertia
1102 + window.addEventListener('wheel', handleScroll, { passive: false });
1103 +
1104 + // Scroll inertia implementation
1105 + const scrollState = {
1106 + momentum: 0,
1107 + isAnimating: false,
1108 + lastScrollTime: Date.now(),
1109 + scrollAccumulator: 0,
1110 + taskNavigationThreshold: 250 // Pixels needed to move one task
1111 + };
1112 +
1113 + function handleScroll(e) {
1114 + // Prevent the default scroll behavior
1115 + e.preventDefault();
1116 +
1117 + // Get scroll delta and update time
1118 + const now = Date.now();
1119 + const timeDelta = now - scrollState.lastScrollTime;
1120 + scrollState.lastScrollTime = now;
1121 +
1122 + // Calculate momentum (higher for faster scrolls)
1123 + const rawDelta = e.deltaY || e.detail || -e.wheelDelta;
1124 + const delta = Math.sign(rawDelta) * Math.min(Math.abs(rawDelta), 100); // Cap delta
1125 +
1126 + // Add to momentum based on time between scrolls (faster scrolls = more momentum)
1127 + if (timeDelta < 100) {
1128 + // Rapid scrolling gets more momentum
1129 + scrollState.momentum += delta * (1 + (100 - timeDelta) / 100);
1130 + } else {
1131 + // Slower scrolling gets normal momentum
1132 + scrollState.momentum += delta;
1133 + }
1134 +
1135 + // Start animation if not already running
1136 + if (!scrollState.isAnimating) {
1137 + scrollState.isAnimating = true;
1138 + animateMomentumScroll();
1139 + }
1140 + }
1141 +
1142 + function animateMomentumScroll() {
1143 + // If momentum is close to zero, stop animation
1144 + if (Math.abs(scrollState.momentum) < 0.5) {
1145 + scrollState.momentum = 0;
1146 + scrollState.isAnimating = false;
1147 + return;
1148 + }
1149 +
1150 + // Add momentum to accumulator
1151 + scrollState.scrollAccumulator += scrollState.momentum;
1152 +
1153 + // Check if we've crossed the threshold to move tasks
1154 + if (Math.abs(scrollState.scrollAccumulator) >= scrollState.taskNavigationThreshold) {
1155 + const tasksToMove = Math.floor(Math.abs(scrollState.scrollAccumulator) / scrollState.taskNavigationThreshold);
1156 + const direction = scrollState.scrollAccumulator > 0 ? 'down' : 'up';
1157 +
1158 + // Move the specified number of tasks
1159 + for (let i = 0; i < tasksToMove; i++) {
1160 + navigateTasks(direction);
1161 + }
1162 +
1163 + // Remove the used part from accumulator
1164 + scrollState.scrollAccumulator = scrollState.scrollAccumulator % scrollState.taskNavigationThreshold;
1165 + }
1166 +
1167 + // Apply friction to slow down momentum
1168 + scrollState.momentum *= 0.6;
1169 +
1170 + // Continue animation
1171 + requestAnimationFrame(animateMomentumScroll);
1172 + }
1173
1174 getThemesFromCSS();
1175 setInitialTheme();