Repository files navigation

LifeOS

LifeOS is a mobile-first personal system built as a TypeScript React app. It tracks daily routines, task variants, completion streaks, weekly goals, and a private journal with folders, entries, sections, rich text, and attachments.

How The App Fits Together

Browser / installed PWA
-> TanStack Start + React routes
-> Supabase JS client
-> Supabase Auth, Postgres, and Storage
Build and hosting
-> Vite
-> TanStack Start server entry
-> Cloudflare Worker runtime through Wrangler

There is no separate Express, Next.js API route layer, or custom Node backend in this repo. The backend is Supabase, and the web runtime is TanStack Start built for Cloudflare. Most application data reads and writes happen directly from React code through @supabase/supabase-js, protected by Supabase row-level security policies.

Core Stack

AreaTechnologyHow it is used
LanguageTypeScriptMain app language for routes, components, Supabase types, and utilities.
UI runtimeReact 19Component model and client interaction state.
Routing/app frameworkTanStack Router + TanStack StartFile-based routes in src/routes, generated route tree in src/routeTree.gen.ts, app shell in src/routes/__root.tsx, router setup in src/router.tsx.
Bundler/dev serverVite 7Local dev server, production build, plugin pipeline.
StylingTailwind CSS 4Utility styling loaded from src/styles.css using Tailwind v4 CSS-first setup.
UI component styleshadcn-style local componentscomponents.json uses the new-york style, Tailwind CSS variables, and Lucide icons. Components are local under src/components/ui.
Iconslucide-reactIcons in navigation, forms, habit cards, journal toolbar, settings, and stats.
AuthSupabase AuthEmail/password signup, signin, signout, session persistence.
DatabaseSupabase PostgresRoutine, completions, journal, attachment metadata, profiles, and weekly goals.
StorageSupabase StoragePrivate journal-attachments bucket for uploaded journal files/images.
Hosting targetCloudflare Worker@cloudflare/vite-plugin, wrangler.jsonc, and generated dist/server/wrangler.json.
PWAWeb manifest + service workerpublic/manifest.webmanifest and public/sw.js make the app installable and cache the app shell.
Datesdate-fnsRecurring schedule math, week navigation, calendar dates, streak windows, labels.
ToastssonnerSuccess/error notifications across auth, saves, exports, journal, and routine editing.
PDF exportjspdfDynamically imported by src/lib/stats-export.ts for settings-page PDF export.

App Structure

src/
components/
page-header.tsx
theme-toggle.tsx
ui/
app-dialog.tsx
button.tsx
input.tsx
label.tsx
sonner.tsx
integrations/
supabase/
client.ts
types.ts
lib/
auth-context.tsx
schedule.ts
goals-data.ts
habit-detail.ts
journal-data.ts
routine-data.ts
routine-seed.ts
seed-routine.ts
stats-export.ts
streaks.ts
symbols.ts
utils.ts
routes/
__root.tsx
auth.tsx
goals.tsx
grid.tsx
habit.$taskId.tsx
index.tsx
journal.tsx
manage.tsx
settings.tsx
today.tsx
routeTree.gen.ts
router.tsx
styles.css

Routes And Features

RouteFilePurpose
/authsrc/routes/auth.tsxEmail/password signup and signin with Supabase Auth.
/src/routes/index.tsxDaily score dashboard for habits, journaling, and goals with ring-style progress and completion links.
/todaysrc/routes/today.tsxRoutine checklist. Computes the active schedule slot, lists scheduled tasks by time of day, and upserts completions/skips.
/gridsrc/routes/grid.tsxRoutine calendar showing which variant is scheduled for each task and slot.
/statssrc/routes/stats.tsxProgress view with current streak, best streak, and consistency.
/habit/$taskIdsrc/routes/habit.$taskId.tsxIndividual habit detail with calendar and streak runs.
/managesrc/routes/manage.tsxCRUD editor for tasks, variants, steps, colors, time-of-day labels, task order, variant order, and schedules.
/goalssrc/routes/goals.tsxWeekly intention and daily three goals, autosaved into Supabase.
/journalsrc/routes/journal.tsxPrivate journal with folders, entries, sections, rich-text toolbar, search/calendar views, bulk actions, and attachments.
/settingssrc/routes/settings.tsxProfile settings, routine start date, signout, CSV export, and PDF export.

src/routes/__root.tsx wraps the whole app with AuthProvider, the Sonner toaster, PWA registration, route metadata, and the authenticated bottom navigation.

Supabase Backend

The Supabase project id in supabase/config.toml is:

cmhkqczvjabptwtyzsgt

The app uses Supabase for three things:

  1. Auth: users sign up/sign in with email and password.
  2. Postgres: app data is stored in typed tables.
  3. Storage: journal attachments are uploaded to a private bucket.

Database Tables

TablePurpose
profilesOne row per auth user. Stores display_name and routine_start_date. Created automatically on signup by a trigger.
tasksUser-owned routine categories such as oral care, skin care, haircare, shower, etc. Includes color, time of day, and sort order.
task_variantsVariants for a task. Stores symbol, label, steps as JSONB, and sort order.
task_scheduleMaps each task to a variant for each recurring schedule_slot.
completionsActual completion state per user/task/date, including completed/skipped steps, done, skipped, and completed_at.
journal_foldersUser-owned journal folders.
journal_notesEntry-level metadata: title, folder, tags, entry date/time, and legacy content fields.
journal_note_pagesEntry sections with title, heading, HTML content, plain-text content, entry date/time, and sort order.
journal_attachmentsAttachment metadata: filename, MIME type, file size, and Supabase Storage path.
weekly_goalsWeekly intention plus daily goals stored as JSONB.

Security Model

All app tables enable row-level security. Policies restrict rows to auth.uid() = user_id or, for profiles, auth.uid() = id.

The journal-attachments storage bucket is private. Storage policies require authenticated users and keep access inside paths where the first folder segment is the user's id:

journal-attachments/{userId}/{noteId}/{timestamp}-{filename}

Migrations

Migrations live in supabase/migrations and create:

  • Base routine schema, profile trigger, RLS policies, and indexes.
  • A duplicate-task cleanup plus UNIQUE (user_id, name) on tasks.
  • Journal folders, entries, attachments, storage bucket, policies, and indexes.
  • Journal entry sections, section dates/times, headings, and search index updates.
  • Weekly goals table, RLS policy, index, and updated-at trigger.
  • LifeOS naming updates for routine and schedule fields.
  • A skipped flag plus per-step skipped_steps on completions so intentional skips are neutral in score, stats, and streaks.

src/integrations/supabase/types.ts is the generated TypeScript database type file used by the data helpers.

Data Flow

  1. AuthProvider in src/lib/auth-context.tsx initializes the Supabase session, listens for auth changes, and exposes user, session, loading, and signOut.
  2. Auth-protected routes redirect to /auth when no user is loaded.
  3. Data helpers in src/lib/*-data.ts call Supabase tables directly.
  4. The route components keep local UI state and write changes back to Supabase.
  5. Supabase RLS is the main backend authorization boundary.

Examples:

  • Home view combines habit completions, today's journal activity, and today's goals into the daily LifeOS score.
  • Today view fetches routine rows plus the user's profile, computes the current schedule slot, then loads completions for the selected date.
  • Checking a task step optimistically updates local state and upserts into completions.
  • Completing, clearing, or skipping habit sub-steps uses the same optimistic completion upsert path.
  • Manage view edits tasks, task_variants, and task_schedule.
  • Journal uploads files to Supabase Storage and stores file metadata in journal_attachments.
  • Goals autosave with a debounce into weekly_goals.
  • Stats are derived from completions, schedules, and the profile routine start date.

Design System And Styling

Styling is centered in src/styles.css:

  • Tailwind v4 is imported with @import "tailwindcss" source(none) and @source "../src".
  • tw-animate-css is imported for animation utilities.
  • CSS custom properties define light/dark theme tokens, radius tokens, app color tokens, and routine color tokens.
  • Dark mode toggles the .dark class on document.documentElement.
  • ThemeToggle stores the user's preference in localStorage.
  • PageHeader gives primary app views a shared left-aligned title, eyebrow, and action layout.
  • The app loads Inter from Google Fonts with a local @font-face declaration.

Local UI helpers:

  • cn in src/lib/utils.ts combines clsx and tailwind-merge.
  • Button uses @radix-ui/react-slot for asChild and class-variance-authority for variants.
  • Label wraps @radix-ui/react-label.
  • AppConfirmDialog and AppTextDialog are custom modal primitives.
  • Toaster wraps sonner.

PWA Files

public/manifest.webmanifest defines:

  • App name: LifeOS
  • Short name: LifeOS
  • Standalone portrait display
  • Health/lifestyle/productivity categories
  • 192px, 512px, and maskable icons
  • Shortcuts for Today and Progress

public/sw.js:

  • Caches the app shell on install.
  • Deletes old caches on activate.
  • Uses a network-first strategy for same-origin GET requests.
  • Falls back to cached content, then /, when offline.

Package Map

Runtime Dependencies

PackageVersionRole
@cloudflare/vite-plugin^1.25.5Builds TanStack Start for Cloudflare. Enabled during vite build.
@radix-ui/react-label^2.1.8Accessible label primitive used by the local Label component.
@radix-ui/react-slot^1.2.4Slot composition used by the local Button component.
@supabase/supabase-js^2.105.1Supabase Auth, Postgres, and Storage client.
@tailwindcss/vite^4.2.1Tailwind CSS Vite plugin.
@tanstack/react-router^1.168.0File routes, links, navigation, router state, error handling.
@tanstack/react-start^1.167.14App framework and Vite plugin for TanStack Start.
class-variance-authority^0.7.1Variant class definitions for UI components.
clsx^2.1.1Conditional class name composition.
date-fns^4.1.0Date math, formatting, schedule windows, streak windows, journal calendar.
jspdf^4.2.1PDF export from settings. Loaded only when exporting.
lucide-react^0.575.0Icon library across the UI.
react^19.2.0React runtime.
react-dom^19.2.0React DOM rendering.
sonner^2.0.7Toast notifications.
tailwind-merge^3.5.0Merges Tailwind classes safely in cn.
tailwindcss^4.2.1Styling framework.
tw-animate-css^1.3.4Animation CSS utilities imported by src/styles.css.
vite-tsconfig-paths^6.0.2Makes TypeScript path aliases work in Vite.

Development Dependencies

PackageVersionRole
@eslint/js^9.32.0Base ESLint rules.
@types/node^22.16.5Node TypeScript types for config/tooling.
@types/react^19.2.0React TypeScript types.
@types/react-dom^19.2.0React DOM TypeScript types.
@vitejs/plugin-react^5.0.4React plugin for Vite.
eslint^9.32.0Lint runner.
eslint-config-prettier^10.1.1Disables rules that conflict with Prettier.
eslint-plugin-prettier^5.2.6Runs Prettier through ESLint.
eslint-plugin-react-hooks^5.2.0React Hooks lint rules.
eslint-plugin-react-refresh^0.4.20React Refresh lint rule.
globals^15.15.0Browser global definitions for ESLint.
prettier^3.7.3Code formatter.
typescript^5.8.3Type checker/compiler.
typescript-eslint^8.56.1TypeScript ESLint parser and rules.
vite^7.3.1Dev server and build tool.

Important Config Files

FilePurpose
package.jsonScripts, dependency list, ESM package mode, sideEffects: false.
package-lock.jsonnpm lockfile. This repo appears npm-oriented even though bunfig.toml exists.
bunfig.tomlBun install setting: saveTextLockfile = false.
vite.config.tsVite plugins, env injection, aliasing, React dedupe, dev server host/port, Cloudflare build plugin.
tsconfig.jsonStrict TypeScript, React JSX, ES2022 target, bundler module resolution, @/* path alias.
eslint.config.jsFlat ESLint config with TypeScript, React Hooks, React Refresh, Prettier.
.prettierrcPrint width 100, semicolons, double quotes, trailing commas.
components.jsonshadcn-style UI metadata: New York style, TSX, CSS variables, Slate base, Lucide icons.
wrangler.jsoncCloudflare Worker config: app name, compatibility date, Node compatibility flag, TanStack server entry.
supabase/config.tomlSupabase project id.

Environment Variables

The Supabase client reads these names:

VITE_SUPABASE_URL=
VITE_SUPABASE_PUBLISHABLE_KEY=

For SSR/runtime environments, the client also falls back to:

SUPABASE_URL=
SUPABASE_PUBLISHABLE_KEY=

The local .env also contains:

VITE_SUPABASE_PROJECT_ID=

That project id is not read by the app code directly, but it can be useful for Supabase tooling or project metadata.

Running Locally

Install dependencies:

npm install

Start the Vite dev server:

npm run dev

The Vite config uses:

host: ::
port: 8080

So the local app is normally available at:

http://localhost:8080

Build for production:

npm run build

Build in development mode:

npm run build:dev

Preview the built Cloudflare Worker output:

npm run build
npm run preview

npm run start runs the same Wrangler dev command as preview.

Database Setup

This repo includes Supabase migrations, but the Supabase CLI is not listed as an npm script or direct dependency. To apply the migrations, use the Supabase CLI externally or run the SQL files in the Supabase dashboard.

Typical CLI flow:

supabase link --project-ref cmhkqczvjabptwtyzsgt
supabase db push

For a local Supabase stack, use the Supabase CLI's local workflow, then point VITE_SUPABASE_URL and VITE_SUPABASE_PUBLISHABLE_KEY at the local project.

Hosting

This app is configured for Cloudflare through:

  • @cloudflare/vite-plugin in vite.config.ts
  • wrangler.jsonc
  • npm run build
  • npm run preview / npm run start

The production build emits Cloudflare runtime configuration under:

dist/server/wrangler.json

There is no dedicated deploy script in package.json. A manual Wrangler deployment would look like:

npm run build
npx wrangler deploy --config dist/server/wrangler.json

In Cloudflare, configure the Supabase environment variables for the deployed Worker. Because the client code uses VITE_* values and the SSR fallback uses non-VITE_* names, keep both sets available when in doubt:

VITE_SUPABASE_URL
VITE_SUPABASE_PUBLISHABLE_KEY
SUPABASE_URL
SUPABASE_PUBLISHABLE_KEY

Scripts

ScriptCommandWhat it does
npm run devvite devStarts the local Vite dev server on port 8080.
npm run buildWRANGLER_LOG_PATH=.wrangler/logs vite buildBuilds the production TanStack Start/Cloudflare output.
npm run build:devWRANGLER_LOG_PATH=.wrangler/logs vite build --mode developmentBuilds with Vite development mode.
npm run previewwrangler dev --config dist/server/wrangler.jsonRuns the built app locally in Wrangler. Build first.
npm run startwrangler dev --config dist/server/wrangler.jsonSame as preview.
npm run linteslint .Runs ESLint.
npm run formatprettier --write .Formats files with Prettier.

Notes And Gaps

  • There is no test script configured in package.json.
  • There is no explicit deploy script, only build and Wrangler preview/start.
  • seedRoutineIfEmpty exists in src/lib/seed-routine.ts, but it is not currently imported by any route. Treat it as available seed logic, not active signup behavior.
  • The app is private/auth-first: authenticated users get bottom navigation and app routes; unauthenticated users are sent to /auth.
  • routeTree.gen.ts is generated TanStack Router output. Do not hand-edit it.

About

A place for me to task manage and track things to offload from my brain, specifically beauty maintenance and hygiene routines

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

LifeOS

LifeOS is a mobile-first personal system built as a TypeScript React app. It tracks daily routines, task variants, completion streaks, weekly goals, and a private journal with folders, entries, sections, rich text, and attachments.

How The App Fits Together

Browser / installed PWA
-> TanStack Start + React routes
-> Supabase JS client
-> Supabase Auth, Postgres, and Storage
Build and hosting
-> Vite
-> TanStack Start server entry
-> Cloudflare Worker runtime through Wrangler

There is no separate Express, Next.js API route layer, or custom Node backend in this repo. The backend is Supabase, and the web runtime is TanStack Start built for Cloudflare. Most application data reads and writes happen directly from React code through @supabase/supabase-js, protected by Supabase row-level security policies.

Core Stack

AreaTechnologyHow it is used
LanguageTypeScriptMain app language for routes, components, Supabase types, and utilities.
UI runtimeReact 19Component model and client interaction state.
Routing/app frameworkTanStack Router + TanStack StartFile-based routes in src/routes, generated route tree in src/routeTree.gen.ts, app shell in src/routes/__root.tsx, router setup in src/router.tsx.
Bundler/dev serverVite 7Local dev server, production build, plugin pipeline.
StylingTailwind CSS 4Utility styling loaded from src/styles.css using Tailwind v4 CSS-first setup.
UI component styleshadcn-style local componentscomponents.json uses the new-york style, Tailwind CSS variables, and Lucide icons. Components are local under src/components/ui.
Iconslucide-reactIcons in navigation, forms, habit cards, journal toolbar, settings, and stats.
AuthSupabase AuthEmail/password signup, signin, signout, session persistence.
DatabaseSupabase PostgresRoutine, completions, journal, attachment metadata, profiles, and weekly goals.
StorageSupabase StoragePrivate journal-attachments bucket for uploaded journal files/images.
Hosting targetCloudflare Worker@cloudflare/vite-plugin, wrangler.jsonc, and generated dist/server/wrangler.json.
PWAWeb manifest + service workerpublic/manifest.webmanifest and public/sw.js make the app installable and cache the app shell.
Datesdate-fnsRecurring schedule math, week navigation, calendar dates, streak windows, labels.
ToastssonnerSuccess/error notifications across auth, saves, exports, journal, and routine editing.
PDF exportjspdfDynamically imported by src/lib/stats-export.ts for settings-page PDF export.

App Structure

src/
components/
page-header.tsx
theme-toggle.tsx
ui/
app-dialog.tsx
button.tsx
input.tsx
label.tsx
sonner.tsx
integrations/
supabase/
client.ts
types.ts
lib/
auth-context.tsx
schedule.ts
goals-data.ts
habit-detail.ts
journal-data.ts
routine-data.ts
routine-seed.ts
seed-routine.ts
stats-export.ts
streaks.ts
symbols.ts
utils.ts
routes/
__root.tsx
auth.tsx
goals.tsx
grid.tsx
habit.$taskId.tsx
index.tsx
journal.tsx
manage.tsx
settings.tsx
today.tsx
routeTree.gen.ts
router.tsx
styles.css

Routes And Features

RouteFilePurpose
/authsrc/routes/auth.tsxEmail/password signup and signin with Supabase Auth.
/src/routes/index.tsxDaily score dashboard for habits, journaling, and goals with ring-style progress and completion links.
/todaysrc/routes/today.tsxRoutine checklist. Computes the active schedule slot, lists scheduled tasks by time of day, and upserts completions/skips.
/gridsrc/routes/grid.tsxRoutine calendar showing which variant is scheduled for each task and slot.
/statssrc/routes/stats.tsxProgress view with current streak, best streak, and consistency.
/habit/$taskIdsrc/routes/habit.$taskId.tsxIndividual habit detail with calendar and streak runs.
/managesrc/routes/manage.tsxCRUD editor for tasks, variants, steps, colors, time-of-day labels, task order, variant order, and schedules.
/goalssrc/routes/goals.tsxWeekly intention and daily three goals, autosaved into Supabase.
/journalsrc/routes/journal.tsxPrivate journal with folders, entries, sections, rich-text toolbar, search/calendar views, bulk actions, and attachments.
/settingssrc/routes/settings.tsxProfile settings, routine start date, signout, CSV export, and PDF export.

src/routes/__root.tsx wraps the whole app with AuthProvider, the Sonner toaster, PWA registration, route metadata, and the authenticated bottom navigation.

Supabase Backend

The Supabase project id in supabase/config.toml is:

cmhkqczvjabptwtyzsgt

The app uses Supabase for three things:

  1. Auth: users sign up/sign in with email and password.
  2. Postgres: app data is stored in typed tables.
  3. Storage: journal attachments are uploaded to a private bucket.

Database Tables

TablePurpose
profilesOne row per auth user. Stores display_name and routine_start_date. Created automatically on signup by a trigger.
tasksUser-owned routine categories such as oral care, skin care, haircare, shower, etc. Includes color, time of day, and sort order.
task_variantsVariants for a task. Stores symbol, label, steps as JSONB, and sort order.
task_scheduleMaps each task to a variant for each recurring schedule_slot.
completionsActual completion state per user/task/date, including completed/skipped steps, done, skipped, and completed_at.
journal_foldersUser-owned journal folders.
journal_notesEntry-level metadata: title, folder, tags, entry date/time, and legacy content fields.
journal_note_pagesEntry sections with title, heading, HTML content, plain-text content, entry date/time, and sort order.
journal_attachmentsAttachment metadata: filename, MIME type, file size, and Supabase Storage path.
weekly_goalsWeekly intention plus daily goals stored as JSONB.

Security Model

All app tables enable row-level security. Policies restrict rows to auth.uid() = user_id or, for profiles, auth.uid() = id.

The journal-attachments storage bucket is private. Storage policies require authenticated users and keep access inside paths where the first folder segment is the user's id:

journal-attachments/{userId}/{noteId}/{timestamp}-{filename}

Migrations

Migrations live in supabase/migrations and create:

  • Base routine schema, profile trigger, RLS policies, and indexes.
  • A duplicate-task cleanup plus UNIQUE (user_id, name) on tasks.
  • Journal folders, entries, attachments, storage bucket, policies, and indexes.
  • Journal entry sections, section dates/times, headings, and search index updates.
  • Weekly goals table, RLS policy, index, and updated-at trigger.
  • LifeOS naming updates for routine and schedule fields.
  • A skipped flag plus per-step skipped_steps on completions so intentional skips are neutral in score, stats, and streaks.

src/integrations/supabase/types.ts is the generated TypeScript database type file used by the data helpers.

Data Flow

  1. AuthProvider in src/lib/auth-context.tsx initializes the Supabase session, listens for auth changes, and exposes user, session, loading, and signOut.
  2. Auth-protected routes redirect to /auth when no user is loaded.
  3. Data helpers in src/lib/*-data.ts call Supabase tables directly.
  4. The route components keep local UI state and write changes back to Supabase.
  5. Supabase RLS is the main backend authorization boundary.

Examples:

  • Home view combines habit completions, today's journal activity, and today's goals into the daily LifeOS score.
  • Today view fetches routine rows plus the user's profile, computes the current schedule slot, then loads completions for the selected date.
  • Checking a task step optimistically updates local state and upserts into completions.
  • Completing, clearing, or skipping habit sub-steps uses the same optimistic completion upsert path.
  • Manage view edits tasks, task_variants, and task_schedule.
  • Journal uploads files to Supabase Storage and stores file metadata in journal_attachments.
  • Goals autosave with a debounce into weekly_goals.
  • Stats are derived from completions, schedules, and the profile routine start date.

Design System And Styling

Styling is centered in src/styles.css:

  • Tailwind v4 is imported with @import "tailwindcss" source(none) and @source "../src".
  • tw-animate-css is imported for animation utilities.
  • CSS custom properties define light/dark theme tokens, radius tokens, app color tokens, and routine color tokens.
  • Dark mode toggles the .dark class on document.documentElement.
  • ThemeToggle stores the user's preference in localStorage.
  • PageHeader gives primary app views a shared left-aligned title, eyebrow, and action layout.
  • The app loads Inter from Google Fonts with a local @font-face declaration.

Local UI helpers:

  • cn in src/lib/utils.ts combines clsx and tailwind-merge.
  • Button uses @radix-ui/react-slot for asChild and class-variance-authority for variants.
  • Label wraps @radix-ui/react-label.
  • AppConfirmDialog and AppTextDialog are custom modal primitives.
  • Toaster wraps sonner.

PWA Files

public/manifest.webmanifest defines:

  • App name: LifeOS
  • Short name: LifeOS
  • Standalone portrait display
  • Health/lifestyle/productivity categories
  • 192px, 512px, and maskable icons
  • Shortcuts for Today and Progress

public/sw.js:

  • Caches the app shell on install.
  • Deletes old caches on activate.
  • Uses a network-first strategy for same-origin GET requests.
  • Falls back to cached content, then /, when offline.

Package Map

Runtime Dependencies

PackageVersionRole
@cloudflare/vite-plugin^1.25.5Builds TanStack Start for Cloudflare. Enabled during vite build.
@radix-ui/react-label^2.1.8Accessible label primitive used by the local Label component.
@radix-ui/react-slot^1.2.4Slot composition used by the local Button component.
@supabase/supabase-js^2.105.1Supabase Auth, Postgres, and Storage client.
@tailwindcss/vite^4.2.1Tailwind CSS Vite plugin.
@tanstack/react-router^1.168.0File routes, links, navigation, router state, error handling.
@tanstack/react-start^1.167.14App framework and Vite plugin for TanStack Start.
class-variance-authority^0.7.1Variant class definitions for UI components.
clsx^2.1.1Conditional class name composition.
date-fns^4.1.0Date math, formatting, schedule windows, streak windows, journal calendar.
jspdf^4.2.1PDF export from settings. Loaded only when exporting.
lucide-react^0.575.0Icon library across the UI.
react^19.2.0React runtime.
react-dom^19.2.0React DOM rendering.
sonner^2.0.7Toast notifications.
tailwind-merge^3.5.0Merges Tailwind classes safely in cn.
tailwindcss^4.2.1Styling framework.
tw-animate-css^1.3.4Animation CSS utilities imported by src/styles.css.
vite-tsconfig-paths^6.0.2Makes TypeScript path aliases work in Vite.

Development Dependencies

PackageVersionRole
@eslint/js^9.32.0Base ESLint rules.
@types/node^22.16.5Node TypeScript types for config/tooling.
@types/react^19.2.0React TypeScript types.
@types/react-dom^19.2.0React DOM TypeScript types.
@vitejs/plugin-react^5.0.4React plugin for Vite.
eslint^9.32.0Lint runner.
eslint-config-prettier^10.1.1Disables rules that conflict with Prettier.
eslint-plugin-prettier^5.2.6Runs Prettier through ESLint.
eslint-plugin-react-hooks^5.2.0React Hooks lint rules.
eslint-plugin-react-refresh^0.4.20React Refresh lint rule.
globals^15.15.0Browser global definitions for ESLint.
prettier^3.7.3Code formatter.
typescript^5.8.3Type checker/compiler.
typescript-eslint^8.56.1TypeScript ESLint parser and rules.
vite^7.3.1Dev server and build tool.

Important Config Files

FilePurpose
package.jsonScripts, dependency list, ESM package mode, sideEffects: false.
package-lock.jsonnpm lockfile. This repo appears npm-oriented even though bunfig.toml exists.
bunfig.tomlBun install setting: saveTextLockfile = false.
vite.config.tsVite plugins, env injection, aliasing, React dedupe, dev server host/port, Cloudflare build plugin.
tsconfig.jsonStrict TypeScript, React JSX, ES2022 target, bundler module resolution, @/* path alias.
eslint.config.jsFlat ESLint config with TypeScript, React Hooks, React Refresh, Prettier.
.prettierrcPrint width 100, semicolons, double quotes, trailing commas.
components.jsonshadcn-style UI metadata: New York style, TSX, CSS variables, Slate base, Lucide icons.
wrangler.jsoncCloudflare Worker config: app name, compatibility date, Node compatibility flag, TanStack server entry.
supabase/config.tomlSupabase project id.

Environment Variables

The Supabase client reads these names:

VITE_SUPABASE_URL=
VITE_SUPABASE_PUBLISHABLE_KEY=

For SSR/runtime environments, the client also falls back to:

SUPABASE_URL=
SUPABASE_PUBLISHABLE_KEY=

The local .env also contains:

VITE_SUPABASE_PROJECT_ID=

That project id is not read by the app code directly, but it can be useful for Supabase tooling or project metadata.

Running Locally

Install dependencies:

npm install

Start the Vite dev server:

npm run dev

The Vite config uses:

host: ::
port: 8080

So the local app is normally available at:

http://localhost:8080

Build for production:

npm run build

Build in development mode:

npm run build:dev

Preview the built Cloudflare Worker output:

npm run build
npm run preview

npm run start runs the same Wrangler dev command as preview.

Database Setup

This repo includes Supabase migrations, but the Supabase CLI is not listed as an npm script or direct dependency. To apply the migrations, use the Supabase CLI externally or run the SQL files in the Supabase dashboard.

Typical CLI flow:

supabase link --project-ref cmhkqczvjabptwtyzsgt
supabase db push

For a local Supabase stack, use the Supabase CLI's local workflow, then point VITE_SUPABASE_URL and VITE_SUPABASE_PUBLISHABLE_KEY at the local project.

Hosting

This app is configured for Cloudflare through:

  • @cloudflare/vite-plugin in vite.config.ts
  • wrangler.jsonc
  • npm run build
  • npm run preview / npm run start

The production build emits Cloudflare runtime configuration under:

dist/server/wrangler.json

There is no dedicated deploy script in package.json. A manual Wrangler deployment would look like:

npm run build
npx wrangler deploy --config dist/server/wrangler.json

In Cloudflare, configure the Supabase environment variables for the deployed Worker. Because the client code uses VITE_* values and the SSR fallback uses non-VITE_* names, keep both sets available when in doubt:

VITE_SUPABASE_URL
VITE_SUPABASE_PUBLISHABLE_KEY
SUPABASE_URL
SUPABASE_PUBLISHABLE_KEY

Scripts

ScriptCommandWhat it does
npm run devvite devStarts the local Vite dev server on port 8080.
npm run buildWRANGLER_LOG_PATH=.wrangler/logs vite buildBuilds the production TanStack Start/Cloudflare output.
npm run build:devWRANGLER_LOG_PATH=.wrangler/logs vite build --mode developmentBuilds with Vite development mode.
npm run previewwrangler dev --config dist/server/wrangler.jsonRuns the built app locally in Wrangler. Build first.
npm run startwrangler dev --config dist/server/wrangler.jsonSame as preview.
npm run linteslint .Runs ESLint.
npm run formatprettier --write .Formats files with Prettier.

Notes And Gaps

  • There is no test script configured in package.json.
  • There is no explicit deploy script, only build and Wrangler preview/start.
  • seedRoutineIfEmpty exists in src/lib/seed-routine.ts, but it is not currently imported by any route. Treat it as available seed logic, not active signup behavior.
  • The app is private/auth-first: authenticated users get bottom navigation and app routes; unauthenticated users are sent to /auth.
  • routeTree.gen.ts is generated TanStack Router output. Do not hand-edit it.

About

A place for me to task manage and track things to offload from my brain, specifically beauty maintenance and hygiene routines

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

LifeOS

LifeOS is a mobile-first personal system built as a TypeScript React app. It tracks daily routines, task variants, completion streaks, weekly goals, and a private journal with folders, entries, sections, rich text, and attachments.

How The App Fits Together

Browser / installed PWA
-> TanStack Start + React routes
-> Supabase JS client
-> Supabase Auth, Postgres, and Storage
Build and hosting
-> Vite
-> TanStack Start server entry
-> Cloudflare Worker runtime through Wrangler

There is no separate Express, Next.js API route layer, or custom Node backend in this repo. The backend is Supabase, and the web runtime is TanStack Start built for Cloudflare. Most application data reads and writes happen directly from React code through @supabase/supabase-js, protected by Supabase row-level security policies.

Core Stack

AreaTechnologyHow it is used
LanguageTypeScriptMain app language for routes, components, Supabase types, and utilities.
UI runtimeReact 19Component model and client interaction state.
Routing/app frameworkTanStack Router + TanStack StartFile-based routes in src/routes, generated route tree in src/routeTree.gen.ts, app shell in src/routes/__root.tsx, router setup in src/router.tsx.
Bundler/dev serverVite 7Local dev server, production build, plugin pipeline.
StylingTailwind CSS 4Utility styling loaded from src/styles.css using Tailwind v4 CSS-first setup.
UI component styleshadcn-style local componentscomponents.json uses the new-york style, Tailwind CSS variables, and Lucide icons. Components are local under src/components/ui.
Iconslucide-reactIcons in navigation, forms, habit cards, journal toolbar, settings, and stats.
AuthSupabase AuthEmail/password signup, signin, signout, session persistence.
DatabaseSupabase PostgresRoutine, completions, journal, attachment metadata, profiles, and weekly goals.
StorageSupabase StoragePrivate journal-attachments bucket for uploaded journal files/images.
Hosting targetCloudflare Worker@cloudflare/vite-plugin, wrangler.jsonc, and generated dist/server/wrangler.json.
PWAWeb manifest + service workerpublic/manifest.webmanifest and public/sw.js make the app installable and cache the app shell.
Datesdate-fnsRecurring schedule math, week navigation, calendar dates, streak windows, labels.
ToastssonnerSuccess/error notifications across auth, saves, exports, journal, and routine editing.
PDF exportjspdfDynamically imported by src/lib/stats-export.ts for settings-page PDF export.

App Structure

src/
components/
page-header.tsx
theme-toggle.tsx
ui/
app-dialog.tsx
button.tsx
input.tsx
label.tsx
sonner.tsx
integrations/
supabase/
client.ts
types.ts
lib/
auth-context.tsx
schedule.ts
goals-data.ts
habit-detail.ts
journal-data.ts
routine-data.ts
routine-seed.ts
seed-routine.ts
stats-export.ts
streaks.ts
symbols.ts
utils.ts
routes/
__root.tsx
auth.tsx
goals.tsx
grid.tsx
habit.$taskId.tsx
index.tsx
journal.tsx
manage.tsx
settings.tsx
today.tsx
routeTree.gen.ts
router.tsx
styles.css

Routes And Features

RouteFilePurpose
/authsrc/routes/auth.tsxEmail/password signup and signin with Supabase Auth.
/src/routes/index.tsxDaily score dashboard for habits, journaling, and goals with ring-style progress and completion links.
/todaysrc/routes/today.tsxRoutine checklist. Computes the active schedule slot, lists scheduled tasks by time of day, and upserts completions/skips.
/gridsrc/routes/grid.tsxRoutine calendar showing which variant is scheduled for each task and slot.
/statssrc/routes/stats.tsxProgress view with current streak, best streak, and consistency.
/habit/$taskIdsrc/routes/habit.$taskId.tsxIndividual habit detail with calendar and streak runs.
/managesrc/routes/manage.tsxCRUD editor for tasks, variants, steps, colors, time-of-day labels, task order, variant order, and schedules.
/goalssrc/routes/goals.tsxWeekly intention and daily three goals, autosaved into Supabase.
/journalsrc/routes/journal.tsxPrivate journal with folders, entries, sections, rich-text toolbar, search/calendar views, bulk actions, and attachments.
/settingssrc/routes/settings.tsxProfile settings, routine start date, signout, CSV export, and PDF export.

src/routes/__root.tsx wraps the whole app with AuthProvider, the Sonner toaster, PWA registration, route metadata, and the authenticated bottom navigation.

Supabase Backend

The Supabase project id in supabase/config.toml is:

cmhkqczvjabptwtyzsgt

The app uses Supabase for three things:

  1. Auth: users sign up/sign in with email and password.
  2. Postgres: app data is stored in typed tables.
  3. Storage: journal attachments are uploaded to a private bucket.

Database Tables

TablePurpose
profilesOne row per auth user. Stores display_name and routine_start_date. Created automatically on signup by a trigger.
tasksUser-owned routine categories such as oral care, skin care, haircare, shower, etc. Includes color, time of day, and sort order.
task_variantsVariants for a task. Stores symbol, label, steps as JSONB, and sort order.
task_scheduleMaps each task to a variant for each recurring schedule_slot.
completionsActual completion state per user/task/date, including completed/skipped steps, done, skipped, and completed_at.
journal_foldersUser-owned journal folders.
journal_notesEntry-level metadata: title, folder, tags, entry date/time, and legacy content fields.
journal_note_pagesEntry sections with title, heading, HTML content, plain-text content, entry date/time, and sort order.
journal_attachmentsAttachment metadata: filename, MIME type, file size, and Supabase Storage path.
weekly_goalsWeekly intention plus daily goals stored as JSONB.

Security Model

All app tables enable row-level security. Policies restrict rows to auth.uid() = user_id or, for profiles, auth.uid() = id.

The journal-attachments storage bucket is private. Storage policies require authenticated users and keep access inside paths where the first folder segment is the user's id:

journal-attachments/{userId}/{noteId}/{timestamp}-{filename}

Migrations

Migrations live in supabase/migrations and create:

  • Base routine schema, profile trigger, RLS policies, and indexes.
  • A duplicate-task cleanup plus UNIQUE (user_id, name) on tasks.
  • Journal folders, entries, attachments, storage bucket, policies, and indexes.
  • Journal entry sections, section dates/times, headings, and search index updates.
  • Weekly goals table, RLS policy, index, and updated-at trigger.
  • LifeOS naming updates for routine and schedule fields.
  • A skipped flag plus per-step skipped_steps on completions so intentional skips are neutral in score, stats, and streaks.

src/integrations/supabase/types.ts is the generated TypeScript database type file used by the data helpers.

Data Flow

  1. AuthProvider in src/lib/auth-context.tsx initializes the Supabase session, listens for auth changes, and exposes user, session, loading, and signOut.
  2. Auth-protected routes redirect to /auth when no user is loaded.
  3. Data helpers in src/lib/*-data.ts call Supabase tables directly.
  4. The route components keep local UI state and write changes back to Supabase.
  5. Supabase RLS is the main backend authorization boundary.

Examples:

  • Home view combines habit completions, today's journal activity, and today's goals into the daily LifeOS score.
  • Today view fetches routine rows plus the user's profile, computes the current schedule slot, then loads completions for the selected date.
  • Checking a task step optimistically updates local state and upserts into completions.
  • Completing, clearing, or skipping habit sub-steps uses the same optimistic completion upsert path.
  • Manage view edits tasks, task_variants, and task_schedule.
  • Journal uploads files to Supabase Storage and stores file metadata in journal_attachments.
  • Goals autosave with a debounce into weekly_goals.
  • Stats are derived from completions, schedules, and the profile routine start date.

Design System And Styling

Styling is centered in src/styles.css:

  • Tailwind v4 is imported with @import "tailwindcss" source(none) and @source "../src".
  • tw-animate-css is imported for animation utilities.
  • CSS custom properties define light/dark theme tokens, radius tokens, app color tokens, and routine color tokens.
  • Dark mode toggles the .dark class on document.documentElement.
  • ThemeToggle stores the user's preference in localStorage.
  • PageHeader gives primary app views a shared left-aligned title, eyebrow, and action layout.
  • The app loads Inter from Google Fonts with a local @font-face declaration.

Local UI helpers:

  • cn in src/lib/utils.ts combines clsx and tailwind-merge.
  • Button uses @radix-ui/react-slot for asChild and class-variance-authority for variants.
  • Label wraps @radix-ui/react-label.
  • AppConfirmDialog and AppTextDialog are custom modal primitives.
  • Toaster wraps sonner.

PWA Files

public/manifest.webmanifest defines:

  • App name: LifeOS
  • Short name: LifeOS
  • Standalone portrait display
  • Health/lifestyle/productivity categories
  • 192px, 512px, and maskable icons
  • Shortcuts for Today and Progress

public/sw.js:

  • Caches the app shell on install.
  • Deletes old caches on activate.
  • Uses a network-first strategy for same-origin GET requests.
  • Falls back to cached content, then /, when offline.

Package Map

Runtime Dependencies

PackageVersionRole
@cloudflare/vite-plugin^1.25.5Builds TanStack Start for Cloudflare. Enabled during vite build.
@radix-ui/react-label^2.1.8Accessible label primitive used by the local Label component.
@radix-ui/react-slot^1.2.4Slot composition used by the local Button component.
@supabase/supabase-js^2.105.1Supabase Auth, Postgres, and Storage client.
@tailwindcss/vite^4.2.1Tailwind CSS Vite plugin.
@tanstack/react-router^1.168.0File routes, links, navigation, router state, error handling.
@tanstack/react-start^1.167.14App framework and Vite plugin for TanStack Start.
class-variance-authority^0.7.1Variant class definitions for UI components.
clsx^2.1.1Conditional class name composition.
date-fns^4.1.0Date math, formatting, schedule windows, streak windows, journal calendar.
jspdf^4.2.1PDF export from settings. Loaded only when exporting.
lucide-react^0.575.0Icon library across the UI.
react^19.2.0React runtime.
react-dom^19.2.0React DOM rendering.
sonner^2.0.7Toast notifications.
tailwind-merge^3.5.0Merges Tailwind classes safely in cn.
tailwindcss^4.2.1Styling framework.
tw-animate-css^1.3.4Animation CSS utilities imported by src/styles.css.
vite-tsconfig-paths^6.0.2Makes TypeScript path aliases work in Vite.

Development Dependencies

PackageVersionRole
@eslint/js^9.32.0Base ESLint rules.
@types/node^22.16.5Node TypeScript types for config/tooling.
@types/react^19.2.0React TypeScript types.
@types/react-dom^19.2.0React DOM TypeScript types.
@vitejs/plugin-react^5.0.4React plugin for Vite.
eslint^9.32.0Lint runner.
eslint-config-prettier^10.1.1Disables rules that conflict with Prettier.
eslint-plugin-prettier^5.2.6Runs Prettier through ESLint.
eslint-plugin-react-hooks^5.2.0React Hooks lint rules.
eslint-plugin-react-refresh^0.4.20React Refresh lint rule.
globals^15.15.0Browser global definitions for ESLint.
prettier^3.7.3Code formatter.
typescript^5.8.3Type checker/compiler.
typescript-eslint^8.56.1TypeScript ESLint parser and rules.
vite^7.3.1Dev server and build tool.

Important Config Files

FilePurpose
package.jsonScripts, dependency list, ESM package mode, sideEffects: false.
package-lock.jsonnpm lockfile. This repo appears npm-oriented even though bunfig.toml exists.
bunfig.tomlBun install setting: saveTextLockfile = false.
vite.config.tsVite plugins, env injection, aliasing, React dedupe, dev server host/port, Cloudflare build plugin.
tsconfig.jsonStrict TypeScript, React JSX, ES2022 target, bundler module resolution, @/* path alias.
eslint.config.jsFlat ESLint config with TypeScript, React Hooks, React Refresh, Prettier.
.prettierrcPrint width 100, semicolons, double quotes, trailing commas.
components.jsonshadcn-style UI metadata: New York style, TSX, CSS variables, Slate base, Lucide icons.
wrangler.jsoncCloudflare Worker config: app name, compatibility date, Node compatibility flag, TanStack server entry.
supabase/config.tomlSupabase project id.

Environment Variables

The Supabase client reads these names:

VITE_SUPABASE_URL=
VITE_SUPABASE_PUBLISHABLE_KEY=

For SSR/runtime environments, the client also falls back to:

SUPABASE_URL=
SUPABASE_PUBLISHABLE_KEY=

The local .env also contains:

VITE_SUPABASE_PROJECT_ID=

That project id is not read by the app code directly, but it can be useful for Supabase tooling or project metadata.

Running Locally

Install dependencies:

npm install

Start the Vite dev server:

npm run dev

The Vite config uses:

host: ::
port: 8080

So the local app is normally available at:

http://localhost:8080

Build for production:

npm run build

Build in development mode:

npm run build:dev

Preview the built Cloudflare Worker output:

npm run build
npm run preview

npm run start runs the same Wrangler dev command as preview.

Database Setup

This repo includes Supabase migrations, but the Supabase CLI is not listed as an npm script or direct dependency. To apply the migrations, use the Supabase CLI externally or run the SQL files in the Supabase dashboard.

Typical CLI flow:

supabase link --project-ref cmhkqczvjabptwtyzsgt
supabase db push

For a local Supabase stack, use the Supabase CLI's local workflow, then point VITE_SUPABASE_URL and VITE_SUPABASE_PUBLISHABLE_KEY at the local project.

Hosting

This app is configured for Cloudflare through:

  • @cloudflare/vite-plugin in vite.config.ts
  • wrangler.jsonc
  • npm run build
  • npm run preview / npm run start

The production build emits Cloudflare runtime configuration under:

dist/server/wrangler.json

There is no dedicated deploy script in package.json. A manual Wrangler deployment would look like:

npm run build
npx wrangler deploy --config dist/server/wrangler.json

In Cloudflare, configure the Supabase environment variables for the deployed Worker. Because the client code uses VITE_* values and the SSR fallback uses non-VITE_* names, keep both sets available when in doubt:

VITE_SUPABASE_URL
VITE_SUPABASE_PUBLISHABLE_KEY
SUPABASE_URL
SUPABASE_PUBLISHABLE_KEY

Scripts

ScriptCommandWhat it does
npm run devvite devStarts the local Vite dev server on port 8080.
npm run buildWRANGLER_LOG_PATH=.wrangler/logs vite buildBuilds the production TanStack Start/Cloudflare output.
npm run build:devWRANGLER_LOG_PATH=.wrangler/logs vite build --mode developmentBuilds with Vite development mode.
npm run previewwrangler dev --config dist/server/wrangler.jsonRuns the built app locally in Wrangler. Build first.
npm run startwrangler dev --config dist/server/wrangler.jsonSame as preview.
npm run linteslint .Runs ESLint.
npm run formatprettier --write .Formats files with Prettier.

Notes And Gaps

  • There is no test script configured in package.json.
  • There is no explicit deploy script, only build and Wrangler preview/start.
  • seedRoutineIfEmpty exists in src/lib/seed-routine.ts, but it is not currently imported by any route. Treat it as available seed logic, not active signup behavior.
  • The app is private/auth-first: authenticated users get bottom navigation and app routes; unauthenticated users are sent to /auth.
  • routeTree.gen.ts is generated TanStack Router output. Do not hand-edit it.

About

A place for me to task manage and track things to offload from my brain, specifically beauty maintenance and hygiene routines

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

LifeOS

LifeOS is a mobile-first personal system built as a TypeScript React app. It tracks daily routines, task variants, completion streaks, weekly goals, and a private journal with folders, entries, sections, rich text, and attachments.

How The App Fits Together

Browser / installed PWA
-> TanStack Start + React routes
-> Supabase JS client
-> Supabase Auth, Postgres, and Storage
Build and hosting
-> Vite
-> TanStack Start server entry
-> Cloudflare Worker runtime through Wrangler

There is no separate Express, Next.js API route layer, or custom Node backend in this repo. The backend is Supabase, and the web runtime is TanStack Start built for Cloudflare. Most application data reads and writes happen directly from React code through @supabase/supabase-js, protected by Supabase row-level security policies.

Core Stack

AreaTechnologyHow it is used
LanguageTypeScriptMain app language for routes, components, Supabase types, and utilities.
UI runtimeReact 19Component model and client interaction state.
Routing/app frameworkTanStack Router + TanStack StartFile-based routes in src/routes, generated route tree in src/routeTree.gen.ts, app shell in src/routes/__root.tsx, router setup in src/router.tsx.
Bundler/dev serverVite 7Local dev server, production build, plugin pipeline.
StylingTailwind CSS 4Utility styling loaded from src/styles.css using Tailwind v4 CSS-first setup.
UI component styleshadcn-style local componentscomponents.json uses the new-york style, Tailwind CSS variables, and Lucide icons. Components are local under src/components/ui.
Iconslucide-reactIcons in navigation, forms, habit cards, journal toolbar, settings, and stats.
AuthSupabase AuthEmail/password signup, signin, signout, session persistence.
DatabaseSupabase PostgresRoutine, completions, journal, attachment metadata, profiles, and weekly goals.
StorageSupabase StoragePrivate journal-attachments bucket for uploaded journal files/images.
Hosting targetCloudflare Worker@cloudflare/vite-plugin, wrangler.jsonc, and generated dist/server/wrangler.json.
PWAWeb manifest + service workerpublic/manifest.webmanifest and public/sw.js make the app installable and cache the app shell.
Datesdate-fnsRecurring schedule math, week navigation, calendar dates, streak windows, labels.
ToastssonnerSuccess/error notifications across auth, saves, exports, journal, and routine editing.
PDF exportjspdfDynamically imported by src/lib/stats-export.ts for settings-page PDF export.

App Structure

src/
components/
page-header.tsx
theme-toggle.tsx
ui/
app-dialog.tsx
button.tsx
input.tsx
label.tsx
sonner.tsx
integrations/
supabase/
client.ts
types.ts
lib/
auth-context.tsx
schedule.ts
goals-data.ts
habit-detail.ts
journal-data.ts
routine-data.ts
routine-seed.ts
seed-routine.ts
stats-export.ts
streaks.ts
symbols.ts
utils.ts
routes/
__root.tsx
auth.tsx
goals.tsx
grid.tsx
habit.$taskId.tsx
index.tsx
journal.tsx
manage.tsx
settings.tsx
today.tsx
routeTree.gen.ts
router.tsx
styles.css

Routes And Features

RouteFilePurpose
/authsrc/routes/auth.tsxEmail/password signup and signin with Supabase Auth.
/src/routes/index.tsxDaily score dashboard for habits, journaling, and goals with ring-style progress and completion links.
/todaysrc/routes/today.tsxRoutine checklist. Computes the active schedule slot, lists scheduled tasks by time of day, and upserts completions/skips.
/gridsrc/routes/grid.tsxRoutine calendar showing which variant is scheduled for each task and slot.
/statssrc/routes/stats.tsxProgress view with current streak, best streak, and consistency.
/habit/$taskIdsrc/routes/habit.$taskId.tsxIndividual habit detail with calendar and streak runs.
/managesrc/routes/manage.tsxCRUD editor for tasks, variants, steps, colors, time-of-day labels, task order, variant order, and schedules.
/goalssrc/routes/goals.tsxWeekly intention and daily three goals, autosaved into Supabase.
/journalsrc/routes/journal.tsxPrivate journal with folders, entries, sections, rich-text toolbar, search/calendar views, bulk actions, and attachments.
/settingssrc/routes/settings.tsxProfile settings, routine start date, signout, CSV export, and PDF export.

src/routes/__root.tsx wraps the whole app with AuthProvider, the Sonner toaster, PWA registration, route metadata, and the authenticated bottom navigation.

Supabase Backend

The Supabase project id in supabase/config.toml is:

cmhkqczvjabptwtyzsgt

The app uses Supabase for three things:

  1. Auth: users sign up/sign in with email and password.
  2. Postgres: app data is stored in typed tables.
  3. Storage: journal attachments are uploaded to a private bucket.

Database Tables

TablePurpose
profilesOne row per auth user. Stores display_name and routine_start_date. Created automatically on signup by a trigger.
tasksUser-owned routine categories such as oral care, skin care, haircare, shower, etc. Includes color, time of day, and sort order.
task_variantsVariants for a task. Stores symbol, label, steps as JSONB, and sort order.
task_scheduleMaps each task to a variant for each recurring schedule_slot.
completionsActual completion state per user/task/date, including completed/skipped steps, done, skipped, and completed_at.
journal_foldersUser-owned journal folders.
journal_notesEntry-level metadata: title, folder, tags, entry date/time, and legacy content fields.
journal_note_pagesEntry sections with title, heading, HTML content, plain-text content, entry date/time, and sort order.
journal_attachmentsAttachment metadata: filename, MIME type, file size, and Supabase Storage path.
weekly_goalsWeekly intention plus daily goals stored as JSONB.

Security Model

All app tables enable row-level security. Policies restrict rows to auth.uid() = user_id or, for profiles, auth.uid() = id.

The journal-attachments storage bucket is private. Storage policies require authenticated users and keep access inside paths where the first folder segment is the user's id:

journal-attachments/{userId}/{noteId}/{timestamp}-{filename}

Migrations

Migrations live in supabase/migrations and create:

  • Base routine schema, profile trigger, RLS policies, and indexes.
  • A duplicate-task cleanup plus UNIQUE (user_id, name) on tasks.
  • Journal folders, entries, attachments, storage bucket, policies, and indexes.
  • Journal entry sections, section dates/times, headings, and search index updates.
  • Weekly goals table, RLS policy, index, and updated-at trigger.
  • LifeOS naming updates for routine and schedule fields.
  • A skipped flag plus per-step skipped_steps on completions so intentional skips are neutral in score, stats, and streaks.

src/integrations/supabase/types.ts is the generated TypeScript database type file used by the data helpers.

Data Flow

  1. AuthProvider in src/lib/auth-context.tsx initializes the Supabase session, listens for auth changes, and exposes user, session, loading, and signOut.
  2. Auth-protected routes redirect to /auth when no user is loaded.
  3. Data helpers in src/lib/*-data.ts call Supabase tables directly.
  4. The route components keep local UI state and write changes back to Supabase.
  5. Supabase RLS is the main backend authorization boundary.

Examples:

  • Home view combines habit completions, today's journal activity, and today's goals into the daily LifeOS score.
  • Today view fetches routine rows plus the user's profile, computes the current schedule slot, then loads completions for the selected date.
  • Checking a task step optimistically updates local state and upserts into completions.
  • Completing, clearing, or skipping habit sub-steps uses the same optimistic completion upsert path.
  • Manage view edits tasks, task_variants, and task_schedule.
  • Journal uploads files to Supabase Storage and stores file metadata in journal_attachments.
  • Goals autosave with a debounce into weekly_goals.
  • Stats are derived from completions, schedules, and the profile routine start date.

Design System And Styling

Styling is centered in src/styles.css:

  • Tailwind v4 is imported with @import "tailwindcss" source(none) and @source "../src".
  • tw-animate-css is imported for animation utilities.
  • CSS custom properties define light/dark theme tokens, radius tokens, app color tokens, and routine color tokens.
  • Dark mode toggles the .dark class on document.documentElement.
  • ThemeToggle stores the user's preference in localStorage.
  • PageHeader gives primary app views a shared left-aligned title, eyebrow, and action layout.
  • The app loads Inter from Google Fonts with a local @font-face declaration.

Local UI helpers:

  • cn in src/lib/utils.ts combines clsx and tailwind-merge.
  • Button uses @radix-ui/react-slot for asChild and class-variance-authority for variants.
  • Label wraps @radix-ui/react-label.
  • AppConfirmDialog and AppTextDialog are custom modal primitives.
  • Toaster wraps sonner.

PWA Files

public/manifest.webmanifest defines:

  • App name: LifeOS
  • Short name: LifeOS
  • Standalone portrait display
  • Health/lifestyle/productivity categories
  • 192px, 512px, and maskable icons
  • Shortcuts for Today and Progress

public/sw.js:

  • Caches the app shell on install.
  • Deletes old caches on activate.
  • Uses a network-first strategy for same-origin GET requests.
  • Falls back to cached content, then /, when offline.

Package Map

Runtime Dependencies

PackageVersionRole
@cloudflare/vite-plugin^1.25.5Builds TanStack Start for Cloudflare. Enabled during vite build.
@radix-ui/react-label^2.1.8Accessible label primitive used by the local Label component.
@radix-ui/react-slot^1.2.4Slot composition used by the local Button component.
@supabase/supabase-js^2.105.1Supabase Auth, Postgres, and Storage client.
@tailwindcss/vite^4.2.1Tailwind CSS Vite plugin.
@tanstack/react-router^1.168.0File routes, links, navigation, router state, error handling.
@tanstack/react-start^1.167.14App framework and Vite plugin for TanStack Start.
class-variance-authority^0.7.1Variant class definitions for UI components.
clsx^2.1.1Conditional class name composition.
date-fns^4.1.0Date math, formatting, schedule windows, streak windows, journal calendar.
jspdf^4.2.1PDF export from settings. Loaded only when exporting.
lucide-react^0.575.0Icon library across the UI.
react^19.2.0React runtime.
react-dom^19.2.0React DOM rendering.
sonner^2.0.7Toast notifications.
tailwind-merge^3.5.0Merges Tailwind classes safely in cn.
tailwindcss^4.2.1Styling framework.
tw-animate-css^1.3.4Animation CSS utilities imported by src/styles.css.
vite-tsconfig-paths^6.0.2Makes TypeScript path aliases work in Vite.

Development Dependencies

PackageVersionRole
@eslint/js^9.32.0Base ESLint rules.
@types/node^22.16.5Node TypeScript types for config/tooling.
@types/react^19.2.0React TypeScript types.
@types/react-dom^19.2.0React DOM TypeScript types.
@vitejs/plugin-react^5.0.4React plugin for Vite.
eslint^9.32.0Lint runner.
eslint-config-prettier^10.1.1Disables rules that conflict with Prettier.
eslint-plugin-prettier^5.2.6Runs Prettier through ESLint.
eslint-plugin-react-hooks^5.2.0React Hooks lint rules.
eslint-plugin-react-refresh^0.4.20React Refresh lint rule.
globals^15.15.0Browser global definitions for ESLint.
prettier^3.7.3Code formatter.
typescript^5.8.3Type checker/compiler.
typescript-eslint^8.56.1TypeScript ESLint parser and rules.
vite^7.3.1Dev server and build tool.

Important Config Files

FilePurpose
package.jsonScripts, dependency list, ESM package mode, sideEffects: false.
package-lock.jsonnpm lockfile. This repo appears npm-oriented even though bunfig.toml exists.
bunfig.tomlBun install setting: saveTextLockfile = false.
vite.config.tsVite plugins, env injection, aliasing, React dedupe, dev server host/port, Cloudflare build plugin.
tsconfig.jsonStrict TypeScript, React JSX, ES2022 target, bundler module resolution, @/* path alias.
eslint.config.jsFlat ESLint config with TypeScript, React Hooks, React Refresh, Prettier.
.prettierrcPrint width 100, semicolons, double quotes, trailing commas.
components.jsonshadcn-style UI metadata: New York style, TSX, CSS variables, Slate base, Lucide icons.
wrangler.jsoncCloudflare Worker config: app name, compatibility date, Node compatibility flag, TanStack server entry.
supabase/config.tomlSupabase project id.

Environment Variables

The Supabase client reads these names:

VITE_SUPABASE_URL=
VITE_SUPABASE_PUBLISHABLE_KEY=

For SSR/runtime environments, the client also falls back to:

SUPABASE_URL=
SUPABASE_PUBLISHABLE_KEY=

The local .env also contains:

VITE_SUPABASE_PROJECT_ID=

That project id is not read by the app code directly, but it can be useful for Supabase tooling or project metadata.

Running Locally

Install dependencies:

npm install

Start the Vite dev server:

npm run dev

The Vite config uses:

host: ::
port: 8080

So the local app is normally available at:

http://localhost:8080

Build for production:

npm run build

Build in development mode:

npm run build:dev

Preview the built Cloudflare Worker output:

npm run build
npm run preview

npm run start runs the same Wrangler dev command as preview.

Database Setup

This repo includes Supabase migrations, but the Supabase CLI is not listed as an npm script or direct dependency. To apply the migrations, use the Supabase CLI externally or run the SQL files in the Supabase dashboard.

Typical CLI flow:

supabase link --project-ref cmhkqczvjabptwtyzsgt
supabase db push

For a local Supabase stack, use the Supabase CLI's local workflow, then point VITE_SUPABASE_URL and VITE_SUPABASE_PUBLISHABLE_KEY at the local project.

Hosting

This app is configured for Cloudflare through:

  • @cloudflare/vite-plugin in vite.config.ts
  • wrangler.jsonc
  • npm run build
  • npm run preview / npm run start

The production build emits Cloudflare runtime configuration under:

dist/server/wrangler.json

There is no dedicated deploy script in package.json. A manual Wrangler deployment would look like:

npm run build
npx wrangler deploy --config dist/server/wrangler.json

In Cloudflare, configure the Supabase environment variables for the deployed Worker. Because the client code uses VITE_* values and the SSR fallback uses non-VITE_* names, keep both sets available when in doubt:

VITE_SUPABASE_URL
VITE_SUPABASE_PUBLISHABLE_KEY
SUPABASE_URL
SUPABASE_PUBLISHABLE_KEY

Scripts

ScriptCommandWhat it does
npm run devvite devStarts the local Vite dev server on port 8080.
npm run buildWRANGLER_LOG_PATH=.wrangler/logs vite buildBuilds the production TanStack Start/Cloudflare output.
npm run build:devWRANGLER_LOG_PATH=.wrangler/logs vite build --mode developmentBuilds with Vite development mode.
npm run previewwrangler dev --config dist/server/wrangler.jsonRuns the built app locally in Wrangler. Build first.
npm run startwrangler dev --config dist/server/wrangler.jsonSame as preview.
npm run linteslint .Runs ESLint.
npm run formatprettier --write .Formats files with Prettier.

Notes And Gaps

  • There is no test script configured in package.json.
  • There is no explicit deploy script, only build and Wrangler preview/start.
  • seedRoutineIfEmpty exists in src/lib/seed-routine.ts, but it is not currently imported by any route. Treat it as available seed logic, not active signup behavior.
  • The app is private/auth-first: authenticated users get bottom navigation and app routes; unauthenticated users are sent to /auth.
  • routeTree.gen.ts is generated TanStack Router output. Do not hand-edit it.

About

A place for me to task manage and track things to offload from my brain, specifically beauty maintenance and hygiene routines

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

LifeOS

LifeOS is a mobile-first personal system built as a TypeScript React app. It tracks daily routines, task variants, completion streaks, weekly goals, and a private journal with folders, entries, sections, rich text, and attachments.

How The App Fits Together

Browser / installed PWA
-> TanStack Start + React routes
-> Supabase JS client
-> Supabase Auth, Postgres, and Storage
Build and hosting
-> Vite
-> TanStack Start server entry
-> Cloudflare Worker runtime through Wrangler

There is no separate Express, Next.js API route layer, or custom Node backend in this repo. The backend is Supabase, and the web runtime is TanStack Start built for Cloudflare. Most application data reads and writes happen directly from React code through @supabase/supabase-js, protected by Supabase row-level security policies.

Core Stack

AreaTechnologyHow it is used
LanguageTypeScriptMain app language for routes, components, Supabase types, and utilities.
UI runtimeReact 19Component model and client interaction state.
Routing/app frameworkTanStack Router + TanStack StartFile-based routes in src/routes, generated route tree in src/routeTree.gen.ts, app shell in src/routes/__root.tsx, router setup in src/router.tsx.
Bundler/dev serverVite 7Local dev server, production build, plugin pipeline.
StylingTailwind CSS 4Utility styling loaded from src/styles.css using Tailwind v4 CSS-first setup.
UI component styleshadcn-style local componentscomponents.json uses the new-york style, Tailwind CSS variables, and Lucide icons. Components are local under src/components/ui.
Iconslucide-reactIcons in navigation, forms, habit cards, journal toolbar, settings, and stats.
AuthSupabase AuthEmail/password signup, signin, signout, session persistence.
DatabaseSupabase PostgresRoutine, completions, journal, attachment metadata, profiles, and weekly goals.
StorageSupabase StoragePrivate journal-attachments bucket for uploaded journal files/images.
Hosting targetCloudflare Worker@cloudflare/vite-plugin, wrangler.jsonc, and generated dist/server/wrangler.json.
PWAWeb manifest + service workerpublic/manifest.webmanifest and public/sw.js make the app installable and cache the app shell.
Datesdate-fnsRecurring schedule math, week navigation, calendar dates, streak windows, labels.
ToastssonnerSuccess/error notifications across auth, saves, exports, journal, and routine editing.
PDF exportjspdfDynamically imported by src/lib/stats-export.ts for settings-page PDF export.

App Structure

src/
components/
page-header.tsx
theme-toggle.tsx
ui/
app-dialog.tsx
button.tsx
input.tsx
label.tsx
sonner.tsx
integrations/
supabase/
client.ts
types.ts
lib/
auth-context.tsx
schedule.ts
goals-data.ts
habit-detail.ts
journal-data.ts
routine-data.ts
routine-seed.ts
seed-routine.ts
stats-export.ts
streaks.ts
symbols.ts
utils.ts
routes/
__root.tsx
auth.tsx
goals.tsx
grid.tsx
habit.$taskId.tsx
index.tsx
journal.tsx
manage.tsx
settings.tsx
today.tsx
routeTree.gen.ts
router.tsx
styles.css

Routes And Features

RouteFilePurpose
/authsrc/routes/auth.tsxEmail/password signup and signin with Supabase Auth.
/src/routes/index.tsxDaily score dashboard for habits, journaling, and goals with ring-style progress and completion links.
/todaysrc/routes/today.tsxRoutine checklist. Computes the active schedule slot, lists scheduled tasks by time of day, and upserts completions/skips.
/gridsrc/routes/grid.tsxRoutine calendar showing which variant is scheduled for each task and slot.
/statssrc/routes/stats.tsxProgress view with current streak, best streak, and consistency.
/habit/$taskIdsrc/routes/habit.$taskId.tsxIndividual habit detail with calendar and streak runs.
/managesrc/routes/manage.tsxCRUD editor for tasks, variants, steps, colors, time-of-day labels, task order, variant order, and schedules.
/goalssrc/routes/goals.tsxWeekly intention and daily three goals, autosaved into Supabase.
/journalsrc/routes/journal.tsxPrivate journal with folders, entries, sections, rich-text toolbar, search/calendar views, bulk actions, and attachments.
/settingssrc/routes/settings.tsxProfile settings, routine start date, signout, CSV export, and PDF export.

src/routes/__root.tsx wraps the whole app with AuthProvider, the Sonner toaster, PWA registration, route metadata, and the authenticated bottom navigation.

Supabase Backend

The Supabase project id in supabase/config.toml is:

cmhkqczvjabptwtyzsgt

The app uses Supabase for three things:

  1. Auth: users sign up/sign in with email and password.
  2. Postgres: app data is stored in typed tables.
  3. Storage: journal attachments are uploaded to a private bucket.

Database Tables

TablePurpose
profilesOne row per auth user. Stores display_name and routine_start_date. Created automatically on signup by a trigger.
tasksUser-owned routine categories such as oral care, skin care, haircare, shower, etc. Includes color, time of day, and sort order.
task_variantsVariants for a task. Stores symbol, label, steps as JSONB, and sort order.
task_scheduleMaps each task to a variant for each recurring schedule_slot.
completionsActual completion state per user/task/date, including completed/skipped steps, done, skipped, and completed_at.
journal_foldersUser-owned journal folders.
journal_notesEntry-level metadata: title, folder, tags, entry date/time, and legacy content fields.
journal_note_pagesEntry sections with title, heading, HTML content, plain-text content, entry date/time, and sort order.
journal_attachmentsAttachment metadata: filename, MIME type, file size, and Supabase Storage path.
weekly_goalsWeekly intention plus daily goals stored as JSONB.

Security Model

All app tables enable row-level security. Policies restrict rows to auth.uid() = user_id or, for profiles, auth.uid() = id.

The journal-attachments storage bucket is private. Storage policies require authenticated users and keep access inside paths where the first folder segment is the user's id:

journal-attachments/{userId}/{noteId}/{timestamp}-{filename}

Migrations

Migrations live in supabase/migrations and create:

  • Base routine schema, profile trigger, RLS policies, and indexes.
  • A duplicate-task cleanup plus UNIQUE (user_id, name) on tasks.
  • Journal folders, entries, attachments, storage bucket, policies, and indexes.
  • Journal entry sections, section dates/times, headings, and search index updates.
  • Weekly goals table, RLS policy, index, and updated-at trigger.
  • LifeOS naming updates for routine and schedule fields.
  • A skipped flag plus per-step skipped_steps on completions so intentional skips are neutral in score, stats, and streaks.

src/integrations/supabase/types.ts is the generated TypeScript database type file used by the data helpers.

Data Flow

  1. AuthProvider in src/lib/auth-context.tsx initializes the Supabase session, listens for auth changes, and exposes user, session, loading, and signOut.
  2. Auth-protected routes redirect to /auth when no user is loaded.
  3. Data helpers in src/lib/*-data.ts call Supabase tables directly.
  4. The route components keep local UI state and write changes back to Supabase.
  5. Supabase RLS is the main backend authorization boundary.

Examples:

  • Home view combines habit completions, today's journal activity, and today's goals into the daily LifeOS score.
  • Today view fetches routine rows plus the user's profile, computes the current schedule slot, then loads completions for the selected date.
  • Checking a task step optimistically updates local state and upserts into completions.
  • Completing, clearing, or skipping habit sub-steps uses the same optimistic completion upsert path.
  • Manage view edits tasks, task_variants, and task_schedule.
  • Journal uploads files to Supabase Storage and stores file metadata in journal_attachments.
  • Goals autosave with a debounce into weekly_goals.
  • Stats are derived from completions, schedules, and the profile routine start date.

Design System And Styling

Styling is centered in src/styles.css:

  • Tailwind v4 is imported with @import "tailwindcss" source(none) and @source "../src".
  • tw-animate-css is imported for animation utilities.
  • CSS custom properties define light/dark theme tokens, radius tokens, app color tokens, and routine color tokens.
  • Dark mode toggles the .dark class on document.documentElement.
  • ThemeToggle stores the user's preference in localStorage.
  • PageHeader gives primary app views a shared left-aligned title, eyebrow, and action layout.
  • The app loads Inter from Google Fonts with a local @font-face declaration.

Local UI helpers:

  • cn in src/lib/utils.ts combines clsx and tailwind-merge.
  • Button uses @radix-ui/react-slot for asChild and class-variance-authority for variants.
  • Label wraps @radix-ui/react-label.
  • AppConfirmDialog and AppTextDialog are custom modal primitives.
  • Toaster wraps sonner.

PWA Files

public/manifest.webmanifest defines:

  • App name: LifeOS
  • Short name: LifeOS
  • Standalone portrait display
  • Health/lifestyle/productivity categories
  • 192px, 512px, and maskable icons
  • Shortcuts for Today and Progress

public/sw.js:

  • Caches the app shell on install.
  • Deletes old caches on activate.
  • Uses a network-first strategy for same-origin GET requests.
  • Falls back to cached content, then /, when offline.

Package Map

Runtime Dependencies

PackageVersionRole
@cloudflare/vite-plugin^1.25.5Builds TanStack Start for Cloudflare. Enabled during vite build.
@radix-ui/react-label^2.1.8Accessible label primitive used by the local Label component.
@radix-ui/react-slot^1.2.4Slot composition used by the local Button component.
@supabase/supabase-js^2.105.1Supabase Auth, Postgres, and Storage client.
@tailwindcss/vite^4.2.1Tailwind CSS Vite plugin.
@tanstack/react-router^1.168.0File routes, links, navigation, router state, error handling.
@tanstack/react-start^1.167.14App framework and Vite plugin for TanStack Start.
class-variance-authority^0.7.1Variant class definitions for UI components.
clsx^2.1.1Conditional class name composition.
date-fns^4.1.0Date math, formatting, schedule windows, streak windows, journal calendar.
jspdf^4.2.1PDF export from settings. Loaded only when exporting.
lucide-react^0.575.0Icon library across the UI.
react^19.2.0React runtime.
react-dom^19.2.0React DOM rendering.
sonner^2.0.7Toast notifications.
tailwind-merge^3.5.0Merges Tailwind classes safely in cn.
tailwindcss^4.2.1Styling framework.
tw-animate-css^1.3.4Animation CSS utilities imported by src/styles.css.
vite-tsconfig-paths^6.0.2Makes TypeScript path aliases work in Vite.

Development Dependencies

PackageVersionRole
@eslint/js^9.32.0Base ESLint rules.
@types/node^22.16.5Node TypeScript types for config/tooling.
@types/react^19.2.0React TypeScript types.
@types/react-dom^19.2.0React DOM TypeScript types.
@vitejs/plugin-react^5.0.4React plugin for Vite.
eslint^9.32.0Lint runner.
eslint-config-prettier^10.1.1Disables rules that conflict with Prettier.
eslint-plugin-prettier^5.2.6Runs Prettier through ESLint.
eslint-plugin-react-hooks^5.2.0React Hooks lint rules.
eslint-plugin-react-refresh^0.4.20React Refresh lint rule.
globals^15.15.0Browser global definitions for ESLint.
prettier^3.7.3Code formatter.
typescript^5.8.3Type checker/compiler.
typescript-eslint^8.56.1TypeScript ESLint parser and rules.
vite^7.3.1Dev server and build tool.

Important Config Files

FilePurpose
package.jsonScripts, dependency list, ESM package mode, sideEffects: false.
package-lock.jsonnpm lockfile. This repo appears npm-oriented even though bunfig.toml exists.
bunfig.tomlBun install setting: saveTextLockfile = false.
vite.config.tsVite plugins, env injection, aliasing, React dedupe, dev server host/port, Cloudflare build plugin.
tsconfig.jsonStrict TypeScript, React JSX, ES2022 target, bundler module resolution, @/* path alias.
eslint.config.jsFlat ESLint config with TypeScript, React Hooks, React Refresh, Prettier.
.prettierrcPrint width 100, semicolons, double quotes, trailing commas.
components.jsonshadcn-style UI metadata: New York style, TSX, CSS variables, Slate base, Lucide icons.
wrangler.jsoncCloudflare Worker config: app name, compatibility date, Node compatibility flag, TanStack server entry.
supabase/config.tomlSupabase project id.

Environment Variables

The Supabase client reads these names:

VITE_SUPABASE_URL=
VITE_SUPABASE_PUBLISHABLE_KEY=

For SSR/runtime environments, the client also falls back to:

SUPABASE_URL=
SUPABASE_PUBLISHABLE_KEY=

The local .env also contains:

VITE_SUPABASE_PROJECT_ID=

That project id is not read by the app code directly, but it can be useful for Supabase tooling or project metadata.

Running Locally

Install dependencies:

npm install

Start the Vite dev server:

npm run dev

The Vite config uses:

host: ::
port: 8080

So the local app is normally available at:

http://localhost:8080

Build for production:

npm run build

Build in development mode:

npm run build:dev

Preview the built Cloudflare Worker output:

npm run build
npm run preview

npm run start runs the same Wrangler dev command as preview.

Database Setup

This repo includes Supabase migrations, but the Supabase CLI is not listed as an npm script or direct dependency. To apply the migrations, use the Supabase CLI externally or run the SQL files in the Supabase dashboard.

Typical CLI flow:

supabase link --project-ref cmhkqczvjabptwtyzsgt
supabase db push

For a local Supabase stack, use the Supabase CLI's local workflow, then point VITE_SUPABASE_URL and VITE_SUPABASE_PUBLISHABLE_KEY at the local project.

Hosting

This app is configured for Cloudflare through:

  • @cloudflare/vite-plugin in vite.config.ts
  • wrangler.jsonc
  • npm run build
  • npm run preview / npm run start

The production build emits Cloudflare runtime configuration under:

dist/server/wrangler.json

There is no dedicated deploy script in package.json. A manual Wrangler deployment would look like:

npm run build
npx wrangler deploy --config dist/server/wrangler.json

In Cloudflare, configure the Supabase environment variables for the deployed Worker. Because the client code uses VITE_* values and the SSR fallback uses non-VITE_* names, keep both sets available when in doubt:

VITE_SUPABASE_URL
VITE_SUPABASE_PUBLISHABLE_KEY
SUPABASE_URL
SUPABASE_PUBLISHABLE_KEY

Scripts

ScriptCommandWhat it does
npm run devvite devStarts the local Vite dev server on port 8080.
npm run buildWRANGLER_LOG_PATH=.wrangler/logs vite buildBuilds the production TanStack Start/Cloudflare output.
npm run build:devWRANGLER_LOG_PATH=.wrangler/logs vite build --mode developmentBuilds with Vite development mode.
npm run previewwrangler dev --config dist/server/wrangler.jsonRuns the built app locally in Wrangler. Build first.
npm run startwrangler dev --config dist/server/wrangler.jsonSame as preview.
npm run linteslint .Runs ESLint.
npm run formatprettier --write .Formats files with Prettier.

Notes And Gaps

  • There is no test script configured in package.json.
  • There is no explicit deploy script, only build and Wrangler preview/start.
  • seedRoutineIfEmpty exists in src/lib/seed-routine.ts, but it is not currently imported by any route. Treat it as available seed logic, not active signup behavior.
  • The app is private/auth-first: authenticated users get bottom navigation and app routes; unauthenticated users are sent to /auth.
  • routeTree.gen.ts is generated TanStack Router output. Do not hand-edit it.

About

A place for me to task manage and track things to offload from my brain, specifically beauty maintenance and hygiene routines

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

LifeOS

LifeOS is a mobile-first personal system built as a TypeScript React app. It tracks daily routines, task variants, completion streaks, weekly goals, and a private journal with folders, entries, sections, rich text, and attachments.

How The App Fits Together

Browser / installed PWA
-> TanStack Start + React routes
-> Supabase JS client
-> Supabase Auth, Postgres, and Storage
Build and hosting
-> Vite
-> TanStack Start server entry
-> Cloudflare Worker runtime through Wrangler

There is no separate Express, Next.js API route layer, or custom Node backend in this repo. The backend is Supabase, and the web runtime is TanStack Start built for Cloudflare. Most application data reads and writes happen directly from React code through @supabase/supabase-js, protected by Supabase row-level security policies.

Core Stack

AreaTechnologyHow it is used
LanguageTypeScriptMain app language for routes, components, Supabase types, and utilities.
UI runtimeReact 19Component model and client interaction state.
Routing/app frameworkTanStack Router + TanStack StartFile-based routes in src/routes, generated route tree in src/routeTree.gen.ts, app shell in src/routes/__root.tsx, router setup in src/router.tsx.
Bundler/dev serverVite 7Local dev server, production build, plugin pipeline.
StylingTailwind CSS 4Utility styling loaded from src/styles.css using Tailwind v4 CSS-first setup.
UI component styleshadcn-style local componentscomponents.json uses the new-york style, Tailwind CSS variables, and Lucide icons. Components are local under src/components/ui.
Iconslucide-reactIcons in navigation, forms, habit cards, journal toolbar, settings, and stats.
AuthSupabase AuthEmail/password signup, signin, signout, session persistence.
DatabaseSupabase PostgresRoutine, completions, journal, attachment metadata, profiles, and weekly goals.
StorageSupabase StoragePrivate journal-attachments bucket for uploaded journal files/images.
Hosting targetCloudflare Worker@cloudflare/vite-plugin, wrangler.jsonc, and generated dist/server/wrangler.json.
PWAWeb manifest + service workerpublic/manifest.webmanifest and public/sw.js make the app installable and cache the app shell.
Datesdate-fnsRecurring schedule math, week navigation, calendar dates, streak windows, labels.
ToastssonnerSuccess/error notifications across auth, saves, exports, journal, and routine editing.
PDF exportjspdfDynamically imported by src/lib/stats-export.ts for settings-page PDF export.

App Structure

src/
components/
page-header.tsx
theme-toggle.tsx
ui/
app-dialog.tsx
button.tsx
input.tsx
label.tsx
sonner.tsx
integrations/
supabase/
client.ts
types.ts
lib/
auth-context.tsx
schedule.ts
goals-data.ts
habit-detail.ts
journal-data.ts
routine-data.ts
routine-seed.ts
seed-routine.ts
stats-export.ts
streaks.ts
symbols.ts
utils.ts
routes/
__root.tsx
auth.tsx
goals.tsx
grid.tsx
habit.$taskId.tsx
index.tsx
journal.tsx
manage.tsx
settings.tsx
today.tsx
routeTree.gen.ts
router.tsx
styles.css

Routes And Features

RouteFilePurpose
/authsrc/routes/auth.tsxEmail/password signup and signin with Supabase Auth.
/src/routes/index.tsxDaily score dashboard for habits, journaling, and goals with ring-style progress and completion links.
/todaysrc/routes/today.tsxRoutine checklist. Computes the active schedule slot, lists scheduled tasks by time of day, and upserts completions/skips.
/gridsrc/routes/grid.tsxRoutine calendar showing which variant is scheduled for each task and slot.
/statssrc/routes/stats.tsxProgress view with current streak, best streak, and consistency.
/habit/$taskIdsrc/routes/habit.$taskId.tsxIndividual habit detail with calendar and streak runs.
/managesrc/routes/manage.tsxCRUD editor for tasks, variants, steps, colors, time-of-day labels, task order, variant order, and schedules.
/goalssrc/routes/goals.tsxWeekly intention and daily three goals, autosaved into Supabase.
/journalsrc/routes/journal.tsxPrivate journal with folders, entries, sections, rich-text toolbar, search/calendar views, bulk actions, and attachments.
/settingssrc/routes/settings.tsxProfile settings, routine start date, signout, CSV export, and PDF export.

src/routes/__root.tsx wraps the whole app with AuthProvider, the Sonner toaster, PWA registration, route metadata, and the authenticated bottom navigation.

Supabase Backend

The Supabase project id in supabase/config.toml is:

cmhkqczvjabptwtyzsgt

The app uses Supabase for three things:

  1. Auth: users sign up/sign in with email and password.
  2. Postgres: app data is stored in typed tables.
  3. Storage: journal attachments are uploaded to a private bucket.

Database Tables

TablePurpose
profilesOne row per auth user. Stores display_name and routine_start_date. Created automatically on signup by a trigger.
tasksUser-owned routine categories such as oral care, skin care, haircare, shower, etc. Includes color, time of day, and sort order.
task_variantsVariants for a task. Stores symbol, label, steps as JSONB, and sort order.
task_scheduleMaps each task to a variant for each recurring schedule_slot.
completionsActual completion state per user/task/date, including completed/skipped steps, done, skipped, and completed_at.
journal_foldersUser-owned journal folders.
journal_notesEntry-level metadata: title, folder, tags, entry date/time, and legacy content fields.
journal_note_pagesEntry sections with title, heading, HTML content, plain-text content, entry date/time, and sort order.
journal_attachmentsAttachment metadata: filename, MIME type, file size, and Supabase Storage path.
weekly_goalsWeekly intention plus daily goals stored as JSONB.

Security Model

All app tables enable row-level security. Policies restrict rows to auth.uid() = user_id or, for profiles, auth.uid() = id.

The journal-attachments storage bucket is private. Storage policies require authenticated users and keep access inside paths where the first folder segment is the user's id:

journal-attachments/{userId}/{noteId}/{timestamp}-{filename}

Migrations

Migrations live in supabase/migrations and create:

  • Base routine schema, profile trigger, RLS policies, and indexes.
  • A duplicate-task cleanup plus UNIQUE (user_id, name) on tasks.
  • Journal folders, entries, attachments, storage bucket, policies, and indexes.
  • Journal entry sections, section dates/times, headings, and search index updates.
  • Weekly goals table, RLS policy, index, and updated-at trigger.
  • LifeOS naming updates for routine and schedule fields.
  • A skipped flag plus per-step skipped_steps on completions so intentional skips are neutral in score, stats, and streaks.

src/integrations/supabase/types.ts is the generated TypeScript database type file used by the data helpers.

Data Flow

  1. AuthProvider in src/lib/auth-context.tsx initializes the Supabase session, listens for auth changes, and exposes user, session, loading, and signOut.
  2. Auth-protected routes redirect to /auth when no user is loaded.
  3. Data helpers in src/lib/*-data.ts call Supabase tables directly.
  4. The route components keep local UI state and write changes back to Supabase.
  5. Supabase RLS is the main backend authorization boundary.

Examples:

  • Home view combines habit completions, today's journal activity, and today's goals into the daily LifeOS score.
  • Today view fetches routine rows plus the user's profile, computes the current schedule slot, then loads completions for the selected date.
  • Checking a task step optimistically updates local state and upserts into completions.
  • Completing, clearing, or skipping habit sub-steps uses the same optimistic completion upsert path.
  • Manage view edits tasks, task_variants, and task_schedule.
  • Journal uploads files to Supabase Storage and stores file metadata in journal_attachments.
  • Goals autosave with a debounce into weekly_goals.
  • Stats are derived from completions, schedules, and the profile routine start date.

Design System And Styling

Styling is centered in src/styles.css:

  • Tailwind v4 is imported with @import "tailwindcss" source(none) and @source "../src".
  • tw-animate-css is imported for animation utilities.
  • CSS custom properties define light/dark theme tokens, radius tokens, app color tokens, and routine color tokens.
  • Dark mode toggles the .dark class on document.documentElement.
  • ThemeToggle stores the user's preference in localStorage.
  • PageHeader gives primary app views a shared left-aligned title, eyebrow, and action layout.
  • The app loads Inter from Google Fonts with a local @font-face declaration.

Local UI helpers:

  • cn in src/lib/utils.ts combines clsx and tailwind-merge.
  • Button uses @radix-ui/react-slot for asChild and class-variance-authority for variants.
  • Label wraps @radix-ui/react-label.
  • AppConfirmDialog and AppTextDialog are custom modal primitives.
  • Toaster wraps sonner.

PWA Files

public/manifest.webmanifest defines:

  • App name: LifeOS
  • Short name: LifeOS
  • Standalone portrait display
  • Health/lifestyle/productivity categories
  • 192px, 512px, and maskable icons
  • Shortcuts for Today and Progress

public/sw.js:

  • Caches the app shell on install.
  • Deletes old caches on activate.
  • Uses a network-first strategy for same-origin GET requests.
  • Falls back to cached content, then /, when offline.

Package Map

Runtime Dependencies

PackageVersionRole
@cloudflare/vite-plugin^1.25.5Builds TanStack Start for Cloudflare. Enabled during vite build.
@radix-ui/react-label^2.1.8Accessible label primitive used by the local Label component.
@radix-ui/react-slot^1.2.4Slot composition used by the local Button component.
@supabase/supabase-js^2.105.1Supabase Auth, Postgres, and Storage client.
@tailwindcss/vite^4.2.1Tailwind CSS Vite plugin.
@tanstack/react-router^1.168.0File routes, links, navigation, router state, error handling.
@tanstack/react-start^1.167.14App framework and Vite plugin for TanStack Start.
class-variance-authority^0.7.1Variant class definitions for UI components.
clsx^2.1.1Conditional class name composition.
date-fns^4.1.0Date math, formatting, schedule windows, streak windows, journal calendar.
jspdf^4.2.1PDF export from settings. Loaded only when exporting.
lucide-react^0.575.0Icon library across the UI.
react^19.2.0React runtime.
react-dom^19.2.0React DOM rendering.
sonner^2.0.7Toast notifications.
tailwind-merge^3.5.0Merges Tailwind classes safely in cn.
tailwindcss^4.2.1Styling framework.
tw-animate-css^1.3.4Animation CSS utilities imported by src/styles.css.
vite-tsconfig-paths^6.0.2Makes TypeScript path aliases work in Vite.

Development Dependencies

PackageVersionRole
@eslint/js^9.32.0Base ESLint rules.
@types/node^22.16.5Node TypeScript types for config/tooling.
@types/react^19.2.0React TypeScript types.
@types/react-dom^19.2.0React DOM TypeScript types.
@vitejs/plugin-react^5.0.4React plugin for Vite.
eslint^9.32.0Lint runner.
eslint-config-prettier^10.1.1Disables rules that conflict with Prettier.
eslint-plugin-prettier^5.2.6Runs Prettier through ESLint.
eslint-plugin-react-hooks^5.2.0React Hooks lint rules.
eslint-plugin-react-refresh^0.4.20React Refresh lint rule.
globals^15.15.0Browser global definitions for ESLint.
prettier^3.7.3Code formatter.
typescript^5.8.3Type checker/compiler.
typescript-eslint^8.56.1TypeScript ESLint parser and rules.
vite^7.3.1Dev server and build tool.

Important Config Files

FilePurpose
package.jsonScripts, dependency list, ESM package mode, sideEffects: false.
package-lock.jsonnpm lockfile. This repo appears npm-oriented even though bunfig.toml exists.
bunfig.tomlBun install setting: saveTextLockfile = false.
vite.config.tsVite plugins, env injection, aliasing, React dedupe, dev server host/port, Cloudflare build plugin.
tsconfig.jsonStrict TypeScript, React JSX, ES2022 target, bundler module resolution, @/* path alias.
eslint.config.jsFlat ESLint config with TypeScript, React Hooks, React Refresh, Prettier.
.prettierrcPrint width 100, semicolons, double quotes, trailing commas.
components.jsonshadcn-style UI metadata: New York style, TSX, CSS variables, Slate base, Lucide icons.
wrangler.jsoncCloudflare Worker config: app name, compatibility date, Node compatibility flag, TanStack server entry.
supabase/config.tomlSupabase project id.

Environment Variables

The Supabase client reads these names:

VITE_SUPABASE_URL=
VITE_SUPABASE_PUBLISHABLE_KEY=

For SSR/runtime environments, the client also falls back to:

SUPABASE_URL=
SUPABASE_PUBLISHABLE_KEY=

The local .env also contains:

VITE_SUPABASE_PROJECT_ID=

That project id is not read by the app code directly, but it can be useful for Supabase tooling or project metadata.

Running Locally

Install dependencies:

npm install

Start the Vite dev server:

npm run dev

The Vite config uses:

host: ::
port: 8080

So the local app is normally available at:

http://localhost:8080

Build for production:

npm run build

Build in development mode:

npm run build:dev

Preview the built Cloudflare Worker output:

npm run build
npm run preview

npm run start runs the same Wrangler dev command as preview.

Database Setup

This repo includes Supabase migrations, but the Supabase CLI is not listed as an npm script or direct dependency. To apply the migrations, use the Supabase CLI externally or run the SQL files in the Supabase dashboard.

Typical CLI flow:

supabase link --project-ref cmhkqczvjabptwtyzsgt
supabase db push

For a local Supabase stack, use the Supabase CLI's local workflow, then point VITE_SUPABASE_URL and VITE_SUPABASE_PUBLISHABLE_KEY at the local project.

Hosting

This app is configured for Cloudflare through:

  • @cloudflare/vite-plugin in vite.config.ts
  • wrangler.jsonc
  • npm run build
  • npm run preview / npm run start

The production build emits Cloudflare runtime configuration under:

dist/server/wrangler.json

There is no dedicated deploy script in package.json. A manual Wrangler deployment would look like:

npm run build
npx wrangler deploy --config dist/server/wrangler.json

In Cloudflare, configure the Supabase environment variables for the deployed Worker. Because the client code uses VITE_* values and the SSR fallback uses non-VITE_* names, keep both sets available when in doubt:

VITE_SUPABASE_URL
VITE_SUPABASE_PUBLISHABLE_KEY
SUPABASE_URL
SUPABASE_PUBLISHABLE_KEY

Scripts

ScriptCommandWhat it does
npm run devvite devStarts the local Vite dev server on port 8080.
npm run buildWRANGLER_LOG_PATH=.wrangler/logs vite buildBuilds the production TanStack Start/Cloudflare output.
npm run build:devWRANGLER_LOG_PATH=.wrangler/logs vite build --mode developmentBuilds with Vite development mode.
npm run previewwrangler dev --config dist/server/wrangler.jsonRuns the built app locally in Wrangler. Build first.
npm run startwrangler dev --config dist/server/wrangler.jsonSame as preview.
npm run linteslint .Runs ESLint.
npm run formatprettier --write .Formats files with Prettier.

Notes And Gaps

  • There is no test script configured in package.json.
  • There is no explicit deploy script, only build and Wrangler preview/start.
  • seedRoutineIfEmpty exists in src/lib/seed-routine.ts, but it is not currently imported by any route. Treat it as available seed logic, not active signup behavior.
  • The app is private/auth-first: authenticated users get bottom navigation and app routes; unauthenticated users are sent to /auth.
  • routeTree.gen.ts is generated TanStack Router output. Do not hand-edit it.

About

A place for me to task manage and track things to offload from my brain, specifically beauty maintenance and hygiene routines

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

LifeOS

LifeOS is a mobile-first personal system built as a TypeScript React app. It tracks daily routines, task variants, completion streaks, weekly goals, and a private journal with folders, entries, sections, rich text, and attachments.

How The App Fits Together

Browser / installed PWA
-> TanStack Start + React routes
-> Supabase JS client
-> Supabase Auth, Postgres, and Storage
Build and hosting
-> Vite
-> TanStack Start server entry
-> Cloudflare Worker runtime through Wrangler

There is no separate Express, Next.js API route layer, or custom Node backend in this repo. The backend is Supabase, and the web runtime is TanStack Start built for Cloudflare. Most application data reads and writes happen directly from React code through @supabase/supabase-js, protected by Supabase row-level security policies.

Core Stack

AreaTechnologyHow it is used
LanguageTypeScriptMain app language for routes, components, Supabase types, and utilities.
UI runtimeReact 19Component model and client interaction state.
Routing/app frameworkTanStack Router + TanStack StartFile-based routes in src/routes, generated route tree in src/routeTree.gen.ts, app shell in src/routes/__root.tsx, router setup in src/router.tsx.
Bundler/dev serverVite 7Local dev server, production build, plugin pipeline.
StylingTailwind CSS 4Utility styling loaded from src/styles.css using Tailwind v4 CSS-first setup.
UI component styleshadcn-style local componentscomponents.json uses the new-york style, Tailwind CSS variables, and Lucide icons. Components are local under src/components/ui.
Iconslucide-reactIcons in navigation, forms, habit cards, journal toolbar, settings, and stats.
AuthSupabase AuthEmail/password signup, signin, signout, session persistence.
DatabaseSupabase PostgresRoutine, completions, journal, attachment metadata, profiles, and weekly goals.
StorageSupabase StoragePrivate journal-attachments bucket for uploaded journal files/images.
Hosting targetCloudflare Worker@cloudflare/vite-plugin, wrangler.jsonc, and generated dist/server/wrangler.json.
PWAWeb manifest + service workerpublic/manifest.webmanifest and public/sw.js make the app installable and cache the app shell.
Datesdate-fnsRecurring schedule math, week navigation, calendar dates, streak windows, labels.
ToastssonnerSuccess/error notifications across auth, saves, exports, journal, and routine editing.
PDF exportjspdfDynamically imported by src/lib/stats-export.ts for settings-page PDF export.

App Structure

src/
components/
page-header.tsx
theme-toggle.tsx
ui/
app-dialog.tsx
button.tsx
input.tsx
label.tsx
sonner.tsx
integrations/
supabase/
client.ts
types.ts
lib/
auth-context.tsx
schedule.ts
goals-data.ts
habit-detail.ts
journal-data.ts
routine-data.ts
routine-seed.ts
seed-routine.ts
stats-export.ts
streaks.ts
symbols.ts
utils.ts
routes/
__root.tsx
auth.tsx
goals.tsx
grid.tsx
habit.$taskId.tsx
index.tsx
journal.tsx
manage.tsx
settings.tsx
today.tsx
routeTree.gen.ts
router.tsx
styles.css

Routes And Features

RouteFilePurpose
/authsrc/routes/auth.tsxEmail/password signup and signin with Supabase Auth.
/src/routes/index.tsxDaily score dashboard for habits, journaling, and goals with ring-style progress and completion links.
/todaysrc/routes/today.tsxRoutine checklist. Computes the active schedule slot, lists scheduled tasks by time of day, and upserts completions/skips.
/gridsrc/routes/grid.tsxRoutine calendar showing which variant is scheduled for each task and slot.
/statssrc/routes/stats.tsxProgress view with current streak, best streak, and consistency.
/habit/$taskIdsrc/routes/habit.$taskId.tsxIndividual habit detail with calendar and streak runs.
/managesrc/routes/manage.tsxCRUD editor for tasks, variants, steps, colors, time-of-day labels, task order, variant order, and schedules.
/goalssrc/routes/goals.tsxWeekly intention and daily three goals, autosaved into Supabase.
/journalsrc/routes/journal.tsxPrivate journal with folders, entries, sections, rich-text toolbar, search/calendar views, bulk actions, and attachments.
/settingssrc/routes/settings.tsxProfile settings, routine start date, signout, CSV export, and PDF export.

src/routes/__root.tsx wraps the whole app with AuthProvider, the Sonner toaster, PWA registration, route metadata, and the authenticated bottom navigation.

Supabase Backend

The Supabase project id in supabase/config.toml is:

cmhkqczvjabptwtyzsgt

The app uses Supabase for three things:

  1. Auth: users sign up/sign in with email and password.
  2. Postgres: app data is stored in typed tables.
  3. Storage: journal attachments are uploaded to a private bucket.

Database Tables

TablePurpose
profilesOne row per auth user. Stores display_name and routine_start_date. Created automatically on signup by a trigger.
tasksUser-owned routine categories such as oral care, skin care, haircare, shower, etc. Includes color, time of day, and sort order.
task_variantsVariants for a task. Stores symbol, label, steps as JSONB, and sort order.
task_scheduleMaps each task to a variant for each recurring schedule_slot.
completionsActual completion state per user/task/date, including completed/skipped steps, done, skipped, and completed_at.
journal_foldersUser-owned journal folders.
journal_notesEntry-level metadata: title, folder, tags, entry date/time, and legacy content fields.
journal_note_pagesEntry sections with title, heading, HTML content, plain-text content, entry date/time, and sort order.
journal_attachmentsAttachment metadata: filename, MIME type, file size, and Supabase Storage path.
weekly_goalsWeekly intention plus daily goals stored as JSONB.

Security Model

All app tables enable row-level security. Policies restrict rows to auth.uid() = user_id or, for profiles, auth.uid() = id.

The journal-attachments storage bucket is private. Storage policies require authenticated users and keep access inside paths where the first folder segment is the user's id:

journal-attachments/{userId}/{noteId}/{timestamp}-{filename}

Migrations

Migrations live in supabase/migrations and create:

  • Base routine schema, profile trigger, RLS policies, and indexes.
  • A duplicate-task cleanup plus UNIQUE (user_id, name) on tasks.
  • Journal folders, entries, attachments, storage bucket, policies, and indexes.
  • Journal entry sections, section dates/times, headings, and search index updates.
  • Weekly goals table, RLS policy, index, and updated-at trigger.
  • LifeOS naming updates for routine and schedule fields.
  • A skipped flag plus per-step skipped_steps on completions so intentional skips are neutral in score, stats, and streaks.

src/integrations/supabase/types.ts is the generated TypeScript database type file used by the data helpers.

Data Flow

  1. AuthProvider in src/lib/auth-context.tsx initializes the Supabase session, listens for auth changes, and exposes user, session, loading, and signOut.
  2. Auth-protected routes redirect to /auth when no user is loaded.
  3. Data helpers in src/lib/*-data.ts call Supabase tables directly.
  4. The route components keep local UI state and write changes back to Supabase.
  5. Supabase RLS is the main backend authorization boundary.

Examples:

  • Home view combines habit completions, today's journal activity, and today's goals into the daily LifeOS score.
  • Today view fetches routine rows plus the user's profile, computes the current schedule slot, then loads completions for the selected date.
  • Checking a task step optimistically updates local state and upserts into completions.
  • Completing, clearing, or skipping habit sub-steps uses the same optimistic completion upsert path.
  • Manage view edits tasks, task_variants, and task_schedule.
  • Journal uploads files to Supabase Storage and stores file metadata in journal_attachments.
  • Goals autosave with a debounce into weekly_goals.
  • Stats are derived from completions, schedules, and the profile routine start date.

Design System And Styling

Styling is centered in src/styles.css:

  • Tailwind v4 is imported with @import "tailwindcss" source(none) and @source "../src".
  • tw-animate-css is imported for animation utilities.
  • CSS custom properties define light/dark theme tokens, radius tokens, app color tokens, and routine color tokens.
  • Dark mode toggles the .dark class on document.documentElement.
  • ThemeToggle stores the user's preference in localStorage.
  • PageHeader gives primary app views a shared left-aligned title, eyebrow, and action layout.
  • The app loads Inter from Google Fonts with a local @font-face declaration.

Local UI helpers:

  • cn in src/lib/utils.ts combines clsx and tailwind-merge.
  • Button uses @radix-ui/react-slot for asChild and class-variance-authority for variants.
  • Label wraps @radix-ui/react-label.
  • AppConfirmDialog and AppTextDialog are custom modal primitives.
  • Toaster wraps sonner.

PWA Files

public/manifest.webmanifest defines:

  • App name: LifeOS
  • Short name: LifeOS
  • Standalone portrait display
  • Health/lifestyle/productivity categories
  • 192px, 512px, and maskable icons
  • Shortcuts for Today and Progress

public/sw.js:

  • Caches the app shell on install.
  • Deletes old caches on activate.
  • Uses a network-first strategy for same-origin GET requests.
  • Falls back to cached content, then /, when offline.

Package Map

Runtime Dependencies

PackageVersionRole
@cloudflare/vite-plugin^1.25.5Builds TanStack Start for Cloudflare. Enabled during vite build.
@radix-ui/react-label^2.1.8Accessible label primitive used by the local Label component.
@radix-ui/react-slot^1.2.4Slot composition used by the local Button component.
@supabase/supabase-js^2.105.1Supabase Auth, Postgres, and Storage client.
@tailwindcss/vite^4.2.1Tailwind CSS Vite plugin.
@tanstack/react-router^1.168.0File routes, links, navigation, router state, error handling.
@tanstack/react-start^1.167.14App framework and Vite plugin for TanStack Start.
class-variance-authority^0.7.1Variant class definitions for UI components.
clsx^2.1.1Conditional class name composition.
date-fns^4.1.0Date math, formatting, schedule windows, streak windows, journal calendar.
jspdf^4.2.1PDF export from settings. Loaded only when exporting.
lucide-react^0.575.0Icon library across the UI.
react^19.2.0React runtime.
react-dom^19.2.0React DOM rendering.
sonner^2.0.7Toast notifications.
tailwind-merge^3.5.0Merges Tailwind classes safely in cn.
tailwindcss^4.2.1Styling framework.
tw-animate-css^1.3.4Animation CSS utilities imported by src/styles.css.
vite-tsconfig-paths^6.0.2Makes TypeScript path aliases work in Vite.

Development Dependencies

PackageVersionRole
@eslint/js^9.32.0Base ESLint rules.
@types/node^22.16.5Node TypeScript types for config/tooling.
@types/react^19.2.0React TypeScript types.
@types/react-dom^19.2.0React DOM TypeScript types.
@vitejs/plugin-react^5.0.4React plugin for Vite.
eslint^9.32.0Lint runner.
eslint-config-prettier^10.1.1Disables rules that conflict with Prettier.
eslint-plugin-prettier^5.2.6Runs Prettier through ESLint.
eslint-plugin-react-hooks^5.2.0React Hooks lint rules.
eslint-plugin-react-refresh^0.4.20React Refresh lint rule.
globals^15.15.0Browser global definitions for ESLint.
prettier^3.7.3Code formatter.
typescript^5.8.3Type checker/compiler.
typescript-eslint^8.56.1TypeScript ESLint parser and rules.
vite^7.3.1Dev server and build tool.

Important Config Files

FilePurpose
package.jsonScripts, dependency list, ESM package mode, sideEffects: false.
package-lock.jsonnpm lockfile. This repo appears npm-oriented even though bunfig.toml exists.
bunfig.tomlBun install setting: saveTextLockfile = false.
vite.config.tsVite plugins, env injection, aliasing, React dedupe, dev server host/port, Cloudflare build plugin.
tsconfig.jsonStrict TypeScript, React JSX, ES2022 target, bundler module resolution, @/* path alias.
eslint.config.jsFlat ESLint config with TypeScript, React Hooks, React Refresh, Prettier.
.prettierrcPrint width 100, semicolons, double quotes, trailing commas.
components.jsonshadcn-style UI metadata: New York style, TSX, CSS variables, Slate base, Lucide icons.
wrangler.jsoncCloudflare Worker config: app name, compatibility date, Node compatibility flag, TanStack server entry.
supabase/config.tomlSupabase project id.

Environment Variables

The Supabase client reads these names:

VITE_SUPABASE_URL=
VITE_SUPABASE_PUBLISHABLE_KEY=

For SSR/runtime environments, the client also falls back to:

SUPABASE_URL=
SUPABASE_PUBLISHABLE_KEY=

The local .env also contains:

VITE_SUPABASE_PROJECT_ID=

That project id is not read by the app code directly, but it can be useful for Supabase tooling or project metadata.

Running Locally

Install dependencies:

npm install

Start the Vite dev server:

npm run dev

The Vite config uses:

host: ::
port: 8080

So the local app is normally available at:

http://localhost:8080

Build for production:

npm run build

Build in development mode:

npm run build:dev

Preview the built Cloudflare Worker output:

npm run build
npm run preview

npm run start runs the same Wrangler dev command as preview.

Database Setup

This repo includes Supabase migrations, but the Supabase CLI is not listed as an npm script or direct dependency. To apply the migrations, use the Supabase CLI externally or run the SQL files in the Supabase dashboard.

Typical CLI flow:

supabase link --project-ref cmhkqczvjabptwtyzsgt
supabase db push

For a local Supabase stack, use the Supabase CLI's local workflow, then point VITE_SUPABASE_URL and VITE_SUPABASE_PUBLISHABLE_KEY at the local project.

Hosting

This app is configured for Cloudflare through:

  • @cloudflare/vite-plugin in vite.config.ts
  • wrangler.jsonc
  • npm run build
  • npm run preview / npm run start

The production build emits Cloudflare runtime configuration under:

dist/server/wrangler.json

There is no dedicated deploy script in package.json. A manual Wrangler deployment would look like:

npm run build
npx wrangler deploy --config dist/server/wrangler.json

In Cloudflare, configure the Supabase environment variables for the deployed Worker. Because the client code uses VITE_* values and the SSR fallback uses non-VITE_* names, keep both sets available when in doubt:

VITE_SUPABASE_URL
VITE_SUPABASE_PUBLISHABLE_KEY
SUPABASE_URL
SUPABASE_PUBLISHABLE_KEY

Scripts

ScriptCommandWhat it does
npm run devvite devStarts the local Vite dev server on port 8080.
npm run buildWRANGLER_LOG_PATH=.wrangler/logs vite buildBuilds the production TanStack Start/Cloudflare output.
npm run build:devWRANGLER_LOG_PATH=.wrangler/logs vite build --mode developmentBuilds with Vite development mode.
npm run previewwrangler dev --config dist/server/wrangler.jsonRuns the built app locally in Wrangler. Build first.
npm run startwrangler dev --config dist/server/wrangler.jsonSame as preview.
npm run linteslint .Runs ESLint.
npm run formatprettier --write .Formats files with Prettier.

Notes And Gaps

  • There is no test script configured in package.json.
  • There is no explicit deploy script, only build and Wrangler preview/start.
  • seedRoutineIfEmpty exists in src/lib/seed-routine.ts, but it is not currently imported by any route. Treat it as available seed logic, not active signup behavior.
  • The app is private/auth-first: authenticated users get bottom navigation and app routes; unauthenticated users are sent to /auth.
  • routeTree.gen.ts is generated TanStack Router output. Do not hand-edit it.

About

A place for me to task manage and track things to offload from my brain, specifically beauty maintenance and hygiene routines

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

LifeOS

LifeOS is a mobile-first personal system built as a TypeScript React app. It tracks daily routines, task variants, completion streaks, weekly goals, and a private journal with folders, entries, sections, rich text, and attachments.

How The App Fits Together

Browser / installed PWA
-> TanStack Start + React routes
-> Supabase JS client
-> Supabase Auth, Postgres, and Storage
Build and hosting
-> Vite
-> TanStack Start server entry
-> Cloudflare Worker runtime through Wrangler

There is no separate Express, Next.js API route layer, or custom Node backend in this repo. The backend is Supabase, and the web runtime is TanStack Start built for Cloudflare. Most application data reads and writes happen directly from React code through @supabase/supabase-js, protected by Supabase row-level security policies.

Core Stack

AreaTechnologyHow it is used
LanguageTypeScriptMain app language for routes, components, Supabase types, and utilities.
UI runtimeReact 19Component model and client interaction state.
Routing/app frameworkTanStack Router + TanStack StartFile-based routes in src/routes, generated route tree in src/routeTree.gen.ts, app shell in src/routes/__root.tsx, router setup in src/router.tsx.
Bundler/dev serverVite 7Local dev server, production build, plugin pipeline.
StylingTailwind CSS 4Utility styling loaded from src/styles.css using Tailwind v4 CSS-first setup.
UI component styleshadcn-style local componentscomponents.json uses the new-york style, Tailwind CSS variables, and Lucide icons. Components are local under src/components/ui.
Iconslucide-reactIcons in navigation, forms, habit cards, journal toolbar, settings, and stats.
AuthSupabase AuthEmail/password signup, signin, signout, session persistence.
DatabaseSupabase PostgresRoutine, completions, journal, attachment metadata, profiles, and weekly goals.
StorageSupabase StoragePrivate journal-attachments bucket for uploaded journal files/images.
Hosting targetCloudflare Worker@cloudflare/vite-plugin, wrangler.jsonc, and generated dist/server/wrangler.json.
PWAWeb manifest + service workerpublic/manifest.webmanifest and public/sw.js make the app installable and cache the app shell.
Datesdate-fnsRecurring schedule math, week navigation, calendar dates, streak windows, labels.
ToastssonnerSuccess/error notifications across auth, saves, exports, journal, and routine editing.
PDF exportjspdfDynamically imported by src/lib/stats-export.ts for settings-page PDF export.

App Structure

src/
components/
page-header.tsx
theme-toggle.tsx
ui/
app-dialog.tsx
button.tsx
input.tsx
label.tsx
sonner.tsx
integrations/
supabase/
client.ts
types.ts
lib/
auth-context.tsx
schedule.ts
goals-data.ts
habit-detail.ts
journal-data.ts
routine-data.ts
routine-seed.ts
seed-routine.ts
stats-export.ts
streaks.ts
symbols.ts
utils.ts
routes/
__root.tsx
auth.tsx
goals.tsx
grid.tsx
habit.$taskId.tsx
index.tsx
journal.tsx
manage.tsx
settings.tsx
today.tsx
routeTree.gen.ts
router.tsx
styles.css

Routes And Features

RouteFilePurpose
/authsrc/routes/auth.tsxEmail/password signup and signin with Supabase Auth.
/src/routes/index.tsxDaily score dashboard for habits, journaling, and goals with ring-style progress and completion links.
/todaysrc/routes/today.tsxRoutine checklist. Computes the active schedule slot, lists scheduled tasks by time of day, and upserts completions/skips.
/gridsrc/routes/grid.tsxRoutine calendar showing which variant is scheduled for each task and slot.
/statssrc/routes/stats.tsxProgress view with current streak, best streak, and consistency.
/habit/$taskIdsrc/routes/habit.$taskId.tsxIndividual habit detail with calendar and streak runs.
/managesrc/routes/manage.tsxCRUD editor for tasks, variants, steps, colors, time-of-day labels, task order, variant order, and schedules.
/goalssrc/routes/goals.tsxWeekly intention and daily three goals, autosaved into Supabase.
/journalsrc/routes/journal.tsxPrivate journal with folders, entries, sections, rich-text toolbar, search/calendar views, bulk actions, and attachments.
/settingssrc/routes/settings.tsxProfile settings, routine start date, signout, CSV export, and PDF export.

src/routes/__root.tsx wraps the whole app with AuthProvider, the Sonner toaster, PWA registration, route metadata, and the authenticated bottom navigation.

Supabase Backend

The Supabase project id in supabase/config.toml is:

cmhkqczvjabptwtyzsgt

The app uses Supabase for three things:

  1. Auth: users sign up/sign in with email and password.
  2. Postgres: app data is stored in typed tables.
  3. Storage: journal attachments are uploaded to a private bucket.

Database Tables

TablePurpose
profilesOne row per auth user. Stores display_name and routine_start_date. Created automatically on signup by a trigger.
tasksUser-owned routine categories such as oral care, skin care, haircare, shower, etc. Includes color, time of day, and sort order.
task_variantsVariants for a task. Stores symbol, label, steps as JSONB, and sort order.
task_scheduleMaps each task to a variant for each recurring schedule_slot.
completionsActual completion state per user/task/date, including completed/skipped steps, done, skipped, and completed_at.
journal_foldersUser-owned journal folders.
journal_notesEntry-level metadata: title, folder, tags, entry date/time, and legacy content fields.
journal_note_pagesEntry sections with title, heading, HTML content, plain-text content, entry date/time, and sort order.
journal_attachmentsAttachment metadata: filename, MIME type, file size, and Supabase Storage path.
weekly_goalsWeekly intention plus daily goals stored as JSONB.

Security Model

All app tables enable row-level security. Policies restrict rows to auth.uid() = user_id or, for profiles, auth.uid() = id.

The journal-attachments storage bucket is private. Storage policies require authenticated users and keep access inside paths where the first folder segment is the user's id:

journal-attachments/{userId}/{noteId}/{timestamp}-{filename}

Migrations

Migrations live in supabase/migrations and create:

  • Base routine schema, profile trigger, RLS policies, and indexes.
  • A duplicate-task cleanup plus UNIQUE (user_id, name) on tasks.
  • Journal folders, entries, attachments, storage bucket, policies, and indexes.
  • Journal entry sections, section dates/times, headings, and search index updates.
  • Weekly goals table, RLS policy, index, and updated-at trigger.
  • LifeOS naming updates for routine and schedule fields.
  • A skipped flag plus per-step skipped_steps on completions so intentional skips are neutral in score, stats, and streaks.

src/integrations/supabase/types.ts is the generated TypeScript database type file used by the data helpers.

Data Flow

  1. AuthProvider in src/lib/auth-context.tsx initializes the Supabase session, listens for auth changes, and exposes user, session, loading, and signOut.
  2. Auth-protected routes redirect to /auth when no user is loaded.
  3. Data helpers in src/lib/*-data.ts call Supabase tables directly.
  4. The route components keep local UI state and write changes back to Supabase.
  5. Supabase RLS is the main backend authorization boundary.

Examples:

  • Home view combines habit completions, today's journal activity, and today's goals into the daily LifeOS score.
  • Today view fetches routine rows plus the user's profile, computes the current schedule slot, then loads completions for the selected date.
  • Checking a task step optimistically updates local state and upserts into completions.
  • Completing, clearing, or skipping habit sub-steps uses the same optimistic completion upsert path.
  • Manage view edits tasks, task_variants, and task_schedule.
  • Journal uploads files to Supabase Storage and stores file metadata in journal_attachments.
  • Goals autosave with a debounce into weekly_goals.
  • Stats are derived from completions, schedules, and the profile routine start date.

Design System And Styling

Styling is centered in src/styles.css:

  • Tailwind v4 is imported with @import "tailwindcss" source(none) and @source "../src".
  • tw-animate-css is imported for animation utilities.
  • CSS custom properties define light/dark theme tokens, radius tokens, app color tokens, and routine color tokens.
  • Dark mode toggles the .dark class on document.documentElement.
  • ThemeToggle stores the user's preference in localStorage.
  • PageHeader gives primary app views a shared left-aligned title, eyebrow, and action layout.
  • The app loads Inter from Google Fonts with a local @font-face declaration.

Local UI helpers:

  • cn in src/lib/utils.ts combines clsx and tailwind-merge.
  • Button uses @radix-ui/react-slot for asChild and class-variance-authority for variants.
  • Label wraps @radix-ui/react-label.
  • AppConfirmDialog and AppTextDialog are custom modal primitives.
  • Toaster wraps sonner.

PWA Files

public/manifest.webmanifest defines:

  • App name: LifeOS
  • Short name: LifeOS
  • Standalone portrait display
  • Health/lifestyle/productivity categories
  • 192px, 512px, and maskable icons
  • Shortcuts for Today and Progress

public/sw.js:

  • Caches the app shell on install.
  • Deletes old caches on activate.
  • Uses a network-first strategy for same-origin GET requests.
  • Falls back to cached content, then /, when offline.

Package Map

Runtime Dependencies

PackageVersionRole
@cloudflare/vite-plugin^1.25.5Builds TanStack Start for Cloudflare. Enabled during vite build.
@radix-ui/react-label^2.1.8Accessible label primitive used by the local Label component.
@radix-ui/react-slot^1.2.4Slot composition used by the local Button component.
@supabase/supabase-js^2.105.1Supabase Auth, Postgres, and Storage client.
@tailwindcss/vite^4.2.1Tailwind CSS Vite plugin.
@tanstack/react-router^1.168.0File routes, links, navigation, router state, error handling.
@tanstack/react-start^1.167.14App framework and Vite plugin for TanStack Start.
class-variance-authority^0.7.1Variant class definitions for UI components.
clsx^2.1.1Conditional class name composition.
date-fns^4.1.0Date math, formatting, schedule windows, streak windows, journal calendar.
jspdf^4.2.1PDF export from settings. Loaded only when exporting.
lucide-react^0.575.0Icon library across the UI.
react^19.2.0React runtime.
react-dom^19.2.0React DOM rendering.
sonner^2.0.7Toast notifications.
tailwind-merge^3.5.0Merges Tailwind classes safely in cn.
tailwindcss^4.2.1Styling framework.
tw-animate-css^1.3.4Animation CSS utilities imported by src/styles.css.
vite-tsconfig-paths^6.0.2Makes TypeScript path aliases work in Vite.

Development Dependencies

PackageVersionRole
@eslint/js^9.32.0Base ESLint rules.
@types/node^22.16.5Node TypeScript types for config/tooling.
@types/react^19.2.0React TypeScript types.
@types/react-dom^19.2.0React DOM TypeScript types.
@vitejs/plugin-react^5.0.4React plugin for Vite.
eslint^9.32.0Lint runner.
eslint-config-prettier^10.1.1Disables rules that conflict with Prettier.
eslint-plugin-prettier^5.2.6Runs Prettier through ESLint.
eslint-plugin-react-hooks^5.2.0React Hooks lint rules.
eslint-plugin-react-refresh^0.4.20React Refresh lint rule.
globals^15.15.0Browser global definitions for ESLint.
prettier^3.7.3Code formatter.
typescript^5.8.3Type checker/compiler.
typescript-eslint^8.56.1TypeScript ESLint parser and rules.
vite^7.3.1Dev server and build tool.

Important Config Files

FilePurpose
package.jsonScripts, dependency list, ESM package mode, sideEffects: false.
package-lock.jsonnpm lockfile. This repo appears npm-oriented even though bunfig.toml exists.
bunfig.tomlBun install setting: saveTextLockfile = false.
vite.config.tsVite plugins, env injection, aliasing, React dedupe, dev server host/port, Cloudflare build plugin.
tsconfig.jsonStrict TypeScript, React JSX, ES2022 target, bundler module resolution, @/* path alias.
eslint.config.jsFlat ESLint config with TypeScript, React Hooks, React Refresh, Prettier.
.prettierrcPrint width 100, semicolons, double quotes, trailing commas.
components.jsonshadcn-style UI metadata: New York style, TSX, CSS variables, Slate base, Lucide icons.
wrangler.jsoncCloudflare Worker config: app name, compatibility date, Node compatibility flag, TanStack server entry.
supabase/config.tomlSupabase project id.

Environment Variables

The Supabase client reads these names:

VITE_SUPABASE_URL=
VITE_SUPABASE_PUBLISHABLE_KEY=

For SSR/runtime environments, the client also falls back to:

SUPABASE_URL=
SUPABASE_PUBLISHABLE_KEY=

The local .env also contains:

VITE_SUPABASE_PROJECT_ID=

That project id is not read by the app code directly, but it can be useful for Supabase tooling or project metadata.

Running Locally

Install dependencies:

npm install

Start the Vite dev server:

npm run dev

The Vite config uses:

host: ::
port: 8080

So the local app is normally available at:

http://localhost:8080

Build for production:

npm run build

Build in development mode:

npm run build:dev

Preview the built Cloudflare Worker output:

npm run build
npm run preview

npm run start runs the same Wrangler dev command as preview.

Database Setup

This repo includes Supabase migrations, but the Supabase CLI is not listed as an npm script or direct dependency. To apply the migrations, use the Supabase CLI externally or run the SQL files in the Supabase dashboard.

Typical CLI flow:

supabase link --project-ref cmhkqczvjabptwtyzsgt
supabase db push

For a local Supabase stack, use the Supabase CLI's local workflow, then point VITE_SUPABASE_URL and VITE_SUPABASE_PUBLISHABLE_KEY at the local project.

Hosting

This app is configured for Cloudflare through:

  • @cloudflare/vite-plugin in vite.config.ts
  • wrangler.jsonc
  • npm run build
  • npm run preview / npm run start

The production build emits Cloudflare runtime configuration under:

dist/server/wrangler.json

There is no dedicated deploy script in package.json. A manual Wrangler deployment would look like:

npm run build
npx wrangler deploy --config dist/server/wrangler.json

In Cloudflare, configure the Supabase environment variables for the deployed Worker. Because the client code uses VITE_* values and the SSR fallback uses non-VITE_* names, keep both sets available when in doubt:

VITE_SUPABASE_URL
VITE_SUPABASE_PUBLISHABLE_KEY
SUPABASE_URL
SUPABASE_PUBLISHABLE_KEY

Scripts

ScriptCommandWhat it does
npm run devvite devStarts the local Vite dev server on port 8080.
npm run buildWRANGLER_LOG_PATH=.wrangler/logs vite buildBuilds the production TanStack Start/Cloudflare output.
npm run build:devWRANGLER_LOG_PATH=.wrangler/logs vite build --mode developmentBuilds with Vite development mode.
npm run previewwrangler dev --config dist/server/wrangler.jsonRuns the built app locally in Wrangler. Build first.
npm run startwrangler dev --config dist/server/wrangler.jsonSame as preview.
npm run linteslint .Runs ESLint.
npm run formatprettier --write .Formats files with Prettier.

Notes And Gaps

  • There is no test script configured in package.json.
  • There is no explicit deploy script, only build and Wrangler preview/start.
  • seedRoutineIfEmpty exists in src/lib/seed-routine.ts, but it is not currently imported by any route. Treat it as available seed logic, not active signup behavior.
  • The app is private/auth-first: authenticated users get bottom navigation and app routes; unauthenticated users are sent to /auth.
  • routeTree.gen.ts is generated TanStack Router output. Do not hand-edit it.

About

A place for me to task manage and track things to offload from my brain, specifically beauty maintenance and hygiene routines

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages