Skip to content

Repository files navigation

DaySet

Targets in, plans out.

DaySet turns a plain list of goals into a structured day — a real timetable with focus blocks, breaks, buffers, a daily priority list, a one-line "quest hook" brief, and a spoken morning greeting. It runs entirely in the browser. Bring your own targets, or paste a sentence in plain English and let the parser turn it into tasks.

The scheduler is a pure function: the same inputs always produce the same plan. That same function powers both the in-app UI and a small, stable SDK that external systems can call directly.


Table of contents


What it does

Most planners ask you to drag blocks around a calendar. DaySet asks the opposite question: given what I'm trying to do this week, what should today look like?

You give it:

  • Targets — name, priority (1–5), estimated duration, preferred time of day, optional deadline, status
  • A time scope — start/end date, daily work window, timezone
  • A user profile — name, wake time, voice, greeting tone, theme
  • Daily context — weather, current datetime, fixed calendar events

And it returns:

  • An interactive timetableDayPlan[], one per day, each with ordered TimeSlot[] (focus / meeting / break / buffer)
  • In-day priorities — the current "now" block, the next blocks, and a ranked priority list with one-line reasons
  • A morning greeting script — text you can have spoken aloud, shaped by your tone preference, name, weather, and the day's plan
  • A daily brief per day — a one-line quest hook plus the top targets and total focus time

Done targets drop out of the schedule automatically. Meetings stay where you put them; everything else flows around them.

Highlights

  • Natural-language entry. Paste "I need to write the quarterly report by Friday, finish the API docs, and reply to Sarah's urgent email tonight" — DaySet splits it into targets, infers priority, duration, deadline and time of day.
  • Boss tasks & XP. Each day's highest-priority focus block is the boss task. Longer focus blocks pay more XP. It's a thin gamification layer that makes the plan feel like a quest, not a checklist.
  • Spoken morning greeting. Wake time, weather and the day's plan become a short script you can play with speechSynthesis. Pick a male, female or neutral voice and a calm / energetic / playful / serious tone.
  • Auto-greet. Optionally auto-play the greeting within a configurable window after your wake time, on the weekdays you choose.
  • Five themes + custom accent. Modern Dark, Techy (neon grid), Flowers (warm plum), Peaceful (muted teal) — or roll your own accent on any base style. Themes affect background, accent, radius and decor.
  • Private by default. Demo mode keeps everything in localStorage. Sign in with email (Supabase Auth) and the same plan syncs to your account across devices.
  • Pure, stable SDK. The same runSdk(input) function the UI calls is exported as a stable contract for external systems. Same inputs, same outputs, every time.

Screenshots / tour

The in-app walkthrough (WalkthroughDialog) steps through the five tabs:

  1. Now — what's happening this minute, what's next, the priority order
  2. Schedule — the multi-day timetable with focus blocks, breaks, buffers and fixed meetings
  3. Briefs — a one-line quest hook and the top targets for any selected day
  4. Greeting — the morning script with a play button and tone preview
  5. Settings — target board, time scope, context, profile, theme, account, data, SDK

The landing page (/) shows the marketing pitch, feature grid, the SDK call-to-action with a link to the SDK docs, and the entry CTAs.

Tech stack

Layer Choice
Framework React 18 + TypeScript 5.9
Bundler Vite 7
Styling Tailwind CSS 4 (@theme tokens, data-theme overrides)
Icons lucide-react
Auth & DB Supabase (auth + Postgres for profiles and targets)
Voice Browser speechSynthesis (no external TTS service)
Persistence localStorage in demo mode, Supabase when signed in
Testing (not yet — see Roadmap)

No backend of our own. The scheduling engine is pure TypeScript and runs entirely on the client.

Quick start

# 1. install
npm install

# 2. run the dev server
npm run dev

# 3. build for production
npm run build
npm run preview

Open the URL Vite prints. The first run boots into a splash, then the landing page. Click Try in demo mode to explore with sample data — no account needed.

If you want signed-in sync, the Supabase project credentials are already wired in src/lib/supabase.ts. The expected schema is documented under Auth & persistence.

Project structure

dayset/
├─ index.html                  # SEO meta + root div
├─ public/
│  ├─ nativelyai.svg           # favicon
│  ├─ robots.txt
│  └─ sitemap.xml
├─ docs/
│  └─ SDK.md                   # full SDK reference (see DaySet SDK below)
├─ src/
│  ├─ App.tsx                  # top-level shell: splash → landing → main app
│  ├─ main.tsx                 # React root
│  ├─ index.css                # Tailwind theme tokens + named theme blocks
│  ├─ types.ts                 # public type contract (Target, DayPlan, SdkInput, …)
│  ├─ data/
│  │  ├─ sample.ts             # sample PlanInputs (demo mode)
│  │  └─ themes.ts             # theme presets + defaults
│  ├─ engine/
│  │  ├─ scheduler.ts          # buildDaySet(inputs) → DaySetOutput  (the core)
│  │  ├─ sdk.ts                # runSdk(input) + helpers + SDK_EXAMPLE_INPUT
│  │  ├─ natural-language.ts   # parseNaturalLanguage(text) → ParsedTodo[]
│  │  ├─ auto-greet.ts         # shouldAutoGreet(profile, now) → decision
│  │  ├─ voice.ts              # speechSynthesis wrapper
│  │  ├─ time.ts               # date / time / duration helpers
│  │  └─ internal-types.ts     # re-exports + Assignment helper type
│  ├─ lib/
│  │  ├─ supabase.ts           # Supabase client
│  │  └─ db.ts                 # loadPlan / savePlan (profile + targets rows)
│  └─ components/
│     ├─ Landing.tsx           # marketing landing page (links to SDK docs)
│     ├─ SdkDocs.tsx           # in-app SDK docs viewer
│     ├─ Splash.tsx            # boot splash
│     ├─ AuthScreen.tsx        # sign-in / sign-up dialog
│     ├─ OnboardingWizard.tsx  # 6-step first-run wizard
│     ├─ Walkthrough.tsx       # in-app feature tour with spotlight
│     ├─ NowStrip.tsx          # "Now" tab — current + next + priorities
│     ├─ Timetable.tsx         # "Schedule" tab — multi-day timetable
│     ├─ Briefs.tsx            # "Briefs" tab — daily quest hooks
│     ├─ GreetingCard.tsx      # "Greeting" tab — script + play button
│     ├─ SetupPanel.tsx        # "Settings" tab — all inputs in one panel
│     └─ ui.tsx                # Chip, Dialog, badges, icons
└─ vite.config.ts              # React + Tailwind + SVGR plugins

The planning engine

The scheduler lives in src/engine/scheduler.ts and exports one main function:

export function buildDaySet(inputs: PlanInputs): DaySetOutput

It works in four passes per day:

  1. Free-range computation. Start from the daily work window. Subtract fixed calendar events. What's left is the free time for that day.
  2. Band allocation. Each target's preferred_time_of_day (morning / afternoon / evening / flexible) maps to a sub-range of the work window. Flexible targets fill wherever there's room.
  3. Block sizing. A target's estimated_duration_minutes is split into focus blocks of at most 90 minutes. After each ~90 minutes of focus, a 10-minute break is inserted; short buffers pad transitions between blocks.
  4. Priority & deadline ordering. Targets are ranked by (priority desc, deadline asc, id). The top-ranked focus block of each day becomes the boss task and earns bonus XP.

XP is sized by block length (small / med / large) and accumulated per day. Energy level (low / med / high) is a gentle hint derived from where the block sits in the day and how much focus came before it.

The in-day priorities view (InDayPriorities) takes the current datetime, finds the slot containing "now", and emits a ranked priority list with one-line reasons computed from deadline distance and status.

The engine is deterministic and side-effect free. The same PlanInputs always produces the same DaySetOutput. This is what makes it safe to expose as an SDK.

Themes & accents

Themes are CSS variable sets in src/index.css, switched by data-theme (and data-base for the custom theme) on the root element. Each theme defines:

  • --color-background, --color-surface, --color-surface-2
  • --color-accent, --color-accent-strong, --color-accent-soft
  • --radius-card, --radius-tile
  • --bg-decor (optional background image — grids, radial blooms)

Accent presets (blue, yellow, mint, coral, custom_hex) are switched by data-accent and override the accent variables. In named themes (Modern Dark, Techy, Flowers, Peaceful) the theme's own accent wins; in Modern Dark the accent preset is applied on top. The custom theme lets you pick any hex on any base style.

Auth & persistence

Two storage modes:

  • Demo mode (default, no sign-in): the plan is persisted to localStorage under dayset.plan.v1. UI state (walkthrough seen?) lives under dayset.ui.v1.
  • Signed-in mode (Supabase Auth, email flow): the plan is loaded from and saved to two Postgres tables, debounced 800 ms after a change.

Expected Supabase schema

-- profiles: one row per user, keyed by auth.users.id
create table public.profiles (
  id uuid primary key references auth.users(id) on delete cascade,
  name text,
  wake_time text,
  preferred_voice text,
  language text,
  theme_accent text,
  custom_hex text,
  theme_name text,
  custom_accent_hex text,
  custom_base_style text,
  greeting_tone text,
  auto_greet_enabled boolean,
  auto_greet_config jsonb,
  onboarding jsonb,
  start_date text,
  end_date text,
  work_window_start text,
  work_window_end text,
  timezone text,
  weather_location text,
  weather_summary text,
  current_datetime text,
  calendar_events jsonb
);

-- targets: one row per target, ordered by position
create table public.targets (
  id text primary key,
  user_id uuid not null references auth.users(id) on delete cascade,
  name text not null,
  priority int not null default 3,
  estimated_duration_minutes int not null default 45,
  preferred_time_of_day text not null default 'flexible',
  deadline_date text,
  status text not null default 'not_started',
  position int not null default 0
);

-- RLS: users can only see and modify their own rows
alter table public.profiles enable row level security;
alter table public.targets  enable row level security;

create policy "own profile" on public.profiles
  for all using (auth.uid() = id) with check (auth.uid() = id);

create policy "own targets" on public.targets
  for all using (auth.uid() = user_id) with check (auth.uid() = user_id);

src/lib/db.ts does the load/save and clamps every field back into the type-safe shape — bad rows from an old schema version fall back to safe defaults instead of crashing the app.

DaySet SDK

The same engine the UI uses is exposed as a small, stable SDK. The full reference lives in docs/SDK.md — the short version:

import { runSdk, SDK_EXAMPLE_INPUT } from "./engine/sdk";

const output = runSdk({
  tasks: [
    {
      id: "t1",
      name: "Ship the API",
      priority: 5,
      estimated_duration_minutes: 120,
      preferred_time_of_day: "morning",
      deadline_date: "2026-08-04",
      status: "not_started",
    },
  ],
  user_profile: {
    name: "Alex",
    wake_time: "06:30",
    preferred_voice: "neutral",
    language: "en-US",
    theme_accent: "blue",
    theme_name: "modern_dark",
    greeting_tone: "calm",
  },
  context: {
    weather_location: "Kampala",
    calendar_events: [],
  },
});

// output.schema_version         → "1.1"
// output.interactive_timetable  → DayPlan[]
// output.in_day_priorities      → { now_block, next_blocks, priority_list, focus_recommendation }
// output.morning_greeting_script → string
// output.daily_brief            → DailyBrief[]

The SDK is pure: no React, no DOM, no network. Run it in a worker, on a server, in another app — anywhere TypeScript runs.

Configuration

The Supabase URL and publishable key are baked into src/lib/supabase.ts. To point at your own project, edit those two constants. No other configuration is required.

The sample data loaded in demo mode lives in src/data/sample.ts. Edit it to change what a fresh demo visitor sees.

The default theme, default greeting tone and default auto-greet weekdays live in src/data/themes.ts.

Scripts

Script What it does
npm run dev Vite dev server with HMR off (intentional)
npm run build Type-check-agnostic production build
npm run preview Serve the production build locally

Type-checking is enforced by tsc --noEmit-equivalent rules in tsconfig.json (strict, noUnusedLocals, noUnusedParameters). Run npx tsc --noEmit to check types without building.

Roadmap

  • Tests. Vitest + unit tests for scheduler, natural-language, time, auto-greet, and the SDK round-trip.
  • CI. GitHub Actions: lint, typecheck, build, test on every PR.
  • ESLint + Prettier. No lint config yet.
  • Weather integration. context.weather_summary is currently free-text. Wire to a real weather API keyed by weather_location.
  • Calendar sync. Pull fixed events from Google Calendar / iCal instead of typing them in.
  • Recurring targets. "Every Monday, review metrics" → auto-create on Monday's plan.
  • Mobile build. PWA manifest + service worker for installable offline use.
  • Multi-user shared plans. Team rooms with a shared target board.

Contributing

This repo is being actively pushed to by an automated conductor-sync workflow. To avoid collisions:

  1. Work on a feature branch (feat/<name> or fix/<name>), not main.
  2. Open a PR. CI will run when it exists.
  3. Keep the engine pure. If you add a side effect to scheduler.ts or sdk.ts, you've broken the SDK contract — don't.

For style: 2-space indent, semi, double quotes for strings, single-line import braces where short. Match the surrounding file.

License

All rights reserved for now. A permissive license will likely be added before any public release.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages