Time & Planning

The parts that make Flux a calendar rather than a list. These components own a day: they place a block at a minute, argue with you when two blocks collide, and keep an immutable record of the plan you locked so the day you had can be compared against the day you intended.

The calendar grid

4 components

A day in Flux is a fixed window: 7am to 10pm, hardcoded as START_HOUR = 7 and TOTAL_HOURS = 15. Everything below places itself inside that window, packs overlapping blocks into columns, and has an opinion about what to do when two things want the same minute.

EntryBlock
One positioned, draggable, resizable block on the grid. The atom of the calendar, and the file that exports the canonical Entry type the rest of the app imports. It carries the most states of anything in Flux.
src/components/calendar/task-block.tsx
Atomic · default
Draft the quarterly review
14:00 - 15:30
Container with children
Deep Work
3
09:00 - 12:00
Conflict · opens the resolve chooser
Standup
09:30 - 09:45
Resolve Conflict
Child · outside its container's window
Write the summary
12:30 - 13:00
Short · under 45 minutes
Coffee
Overlap · packed into columns
1:1
11:00 - 11:30
Planning
11:00 - 12:00

Six of its states are shown; it has more. The block also renders a drag handle on hover, three drag sub-states (move, resize-start, resize-end), and returns null outright when the computed duration is zero or less. The colours are the app's --chart-2 for atomic and --chart-3 for container, assigned at creation time in QuickAddEntry rather than by the block itself.

CalendarView
The calendar workspace itself: it renders the grid, computes overlap columns, and owns drag-to-move and drag-to-resize. At 562 lines it is the largest single surface in the app.
src/components/calendar/calendar-view.tsx
Day view · with the now line
09:00
10:00
11:00
12:00
Deep Work
09:00 - 11:00
1:1
11:05 - 11:50
Loading · entriesLoading
Drag ghost
10:00
11:00
Review the draft
10:00 - 10:45
11:00 - 11:45

The view is a 4-way union: day, week, agenda, circular. It defaults from settings.defaultView but is force-overridden to day on mobile, so the setting silently does nothing on a phone.

AgendaView
A flat chronological list of the day, including entries that were deleted from the locked plan. Deleted rows stay visible and struck through, so a plan you abandoned still reads as something that happened.
src/components/calendar/agenda-view.tsx
Normal row
09:00 - 11:00 Deep Work
Deleted from the locked plan
13:00 - 13:30 Gym
DiffAgendaView
The day you had, marked against the day you planned. This is where the planner's promise gets cashed: each entry is classified as new, modified or unchanged by comparing it to the locked snapshot.
src/components/calendar/agenda-view.tsx
All three states of the union
15:10 - 15:40 Unblock the build new
09:00 - 11:30 Deep Work modified
13:00 - 13:45 Lunch

Unchanged rows carry no icon and no label. That is deliberate in the source: only divergence is annotated, so the eye lands on what moved.

Reading the day a different way

2 components

Two alternate readings of the same entries. The circular view exists to make one thing obvious that a grid hides: how much of the day is not spoken for.

CircularView
The day as a 24-hour donut. Everything you scheduled is a slice; the remainder is synthesised into one "Free Time" slice so unscheduled time is a shape rather than an absence.
src/components/calendar/circular-view.tsx
Populated
Today Thursday

The centre always reads "Today" plus the weekday: it is not date-aware. The Free Time slice is the only one whose tooltip omits a time range, because its start is the literal string "N/A".

CircularView Dead code
A byte-identical duplicate of the calendar's CircularView, sitting loose at the root of the components directory. It has zero importers. Listed rather than dropped so the component count stays reconcilable.
src/components/circular-view.tsx
Not rendered
Nothing imports this file.

The only difference from calendar/circular-view.tsx is the import path and some trailing whitespace. Deleting it is safe; it is shown here because a showcase that quietly omits dead code is a showcase you cannot count.

The Now surface

3 components

Flux's answer to "what am I supposed to be doing". These strip the day down to one block and a countdown, and they are the only place the app deliberately shows you less than it knows.

EntryCard Reproduced bug
The previous / current / next card on the Now page, with dials to nudge the time and a way into focus mode. Rendered here at its real width so the collision noted below is shown at the size the product has it.
src/components/now/entry-card.tsx
Current · isCurrent + onFocusClick
NOW
Draft the quarterly review
Previous or next · no focus button
NEXT
Reply to the vendor

Two findings, both reproduced as shipped. First: the Focus button is absolutely positioned at right-4 over a description row whose time is flushed right, so the 40px button lands exactly on the end time and hides it. Second: the dials look like time controls but only raise a toast reading "Time Adjusted"; they do not move the entry.

FocusView
A full-screen countdown on one task that replaces the entire Now page. The only way out is finishing, closing, or buying yourself five more minutes.
src/components/now/focus-view.tsx
Running
Draft the quarterly review
24:13
Paused
Draft the quarterly review
24:13

Pause does not pause. The timer recomputes from the entry's wall-clock end, so the display freezes while the deadline keeps coming. Resuming shows the time you actually have left, not the time you had when you paused. Overdue clamps to 00:00 rather than counting negative.

QuickAddEntry
Capture from the sidebar without leaving the page. A sheet with the fields you always need, and an advanced panel with the ones you sometimes do.
src/components/shared/quick-add-task.tsx
Sheet · advanced open
Atomic
P2
Length is flexible

The microphone button has no click handler. "Use Voice Input" is decoration in the shipped code. Also worth knowing: the sheet opens from the bottom on mobile and the right on desktop, and switching "Length is flexible" on disables the duration field, because three interlocking effects then derive it from start and end.

Planning

2 components

Flux's central claim is that a plan you can quietly rewrite is not a plan. Locking freezes the day into an immutable snapshot; everything here exists to put that snapshot next to what actually happened.

PlannerView
Plan against reality, side by side. The locked snapshot on the left never moves; the right column is the live day. The two columns load independently, so one can be waiting while the other is already answering.
src/components/planner/planner-view.tsx
Locked · both columns loaded
Planned
09:00Deep Work
13:00Gym
Actual
09:00Deep Workmodified
15:10Unblock the buildnew
Plan not locked · the empty state that matters
Plan Not Locked
Lock the plan for this day to see the snapshot here.
Loading · the two columns are independent
Loading Plan...
Loading Schedule...

The deleted rows in the Planned column are computed, not stored: they are the entries present in the snapshot and absent from today. That is the only place in Flux where deleting something makes it more visible.

EntryDetailsSheet
Everything about one entry: its time, the variables it involves, the Choose Your Hard trade-off fields, and the signal rating that quietly feeds Flux's learning corpus.
src/components/calendar/task-details-sheet.tsx
Scheduled entry with linked variables
Time
14:00 - 15:30

Variables
Priya Raghavan Home office Low energy Reporting · AI

Signal72
Unscheduled · start is 00:00
Reply to the vendor
The Time row and the whole Duration / Share-of-Day card are hidden entirely when an entry is unscheduled.
Child outside its container's window
Time Conflict
This task is outside its container's time window.

The variable picker filters across all five kinds (person, place, tool, topic, condition), and typing a name with no exact match offers to create it as a tag on the spot. Conditions render as outline badges; every other kind is a solid badge. Moving the signal slider writes a correction row on release, not on drag.