Adam Bandel


Task Windows

Nov 2025
Type: web-app
Code: 8k lines
Files: 33
Active: Nov 2025 — Nov 2025
Stack:
TypeScriptNext.js 16React 19ZustandTailwindCSSdnd-kit
Tags:
productivitydeveloper-tools

Overview

Task Windows is a novel approach to task management that organizes work into rolling 3-day time windows rather than traditional single-day calendars. Each window represents a contiguous block of time where tasks can be scheduled, with built-in capacity tracking to prevent overcommitment. The application features a polished drag-and-drop interface with modifier-key workflows for rapid task organization.

The system treats time as a sliding window: today’s window covers the next 3 days, and tasks can span multiple windows to represent longer-duration work. This approach acknowledges that many tasks don’t fit neatly into a single day while maintaining focus on what’s actionable now.

Screenshots

Main Dashboard

Point Assignment Overlay

Sidebar with Heatmap

Problem

Traditional calendar-based task managers force users to assign tasks to specific days, which creates friction when tasks don’t fit neatly into 24-hour blocks. This leads to constant rescheduling as tasks slip from one day to the next. Additionally, most tools lack capacity awareness, making it easy to overcommit without realizing it until burnout sets in.

The goal was to create a task manager that:

Approach

The solution centers on a custom “engine” that generates the visible window state from a flat list of tasks. Each task has a home window index and a span, and the engine projects these into the appropriate windows, creating ghost instances where tasks extend beyond their home.

Stack

Challenges

Outcomes

The time-window mental model proves effective for managing flexible workloads. The capacity visualization catches overcommitment before it becomes a problem, and the ghost-task rendering provides visibility into upcoming work without manual date management.

Key technical wins:

Implementation Notes

The core engine generates window data from tasks:

export function generateEngineState(
    allTasks: Task[], 
    now: Date = new Date(), 
    viewOffset: number = 0,
    settings: Settings,
    contexts: Context[] = [],
    projects: Project[] = [],
    visibleColumns: number = 4,
    dragSourceWindowIndex?: number | null
): AppState {
    // Tasks are distributed to windows based on home_window_index and span_windows
    // Ghost instances created for i !== home where i is in [home, home + span - 1]
    // Capacity calculated per-window and for 3-window clusters
}

The modifier-key collision detection enables different drop behaviors:

const customCollisionDetection: CollisionDetection = useCallback((args) => {
    const pointerCollisions = pointerWithin(args);
    const { isShiftHeld, isCtrlHeld, isAltHeld } = useTaskStore.getState();
    
    // Shift: prioritize point-assignment zones
    if (isShiftHeld && !isCtrlHeld && !isAltHeld) {
        const pointZone = pointerCollisions.find(c => c.id.toString().includes('-pts-'));
        if (pointZone) return [pointZone];
    }
    
    // Ctrl: prioritize context zones
    // Alt: prioritize project zones
    // ...
}, []);

The 4 AM day boundary handles late-night work gracefully:

export function getLogicalDate(now: Date = new Date()): string {
    const zoned = toZonedTime(now, 'America/Chicago');
    const shifted = subHours(zoned, 4); // Work before 4 AM counts as previous day
    return format(shifted, 'yyyy-MM-dd');
}

Related Posts

No posts yet.