Skip to content

Repository files navigation

Aether // Command v3.0

A dense, high-performance personal developer command center. "Spider-Man Noir" design system: grayscale-first, high-contrast chiaroscuro, 1930s art-deco pulp.

This repository is the foundation. Feature modules are built by separate agents into the folders reserved for them in the boundary map below. Respect the boundaries: shared code lives in src/components/ui, src/components/shell, and src/lib; module code lives only in that module's own route folder(s).


Run

npm install
npm run dev     # http://localhost:3000
npm run build   # production build (must stay green)
npm run lint    # eslint (must stay clean)

Stack: Next.js 15 (App Router) · TypeScript · Tailwind v4 · lucide-react · recharts · idb-keyval · zod · date-fns. Do not add dependencies without explicit approval.

.research/ holds API notes dropped by research agents (not shipped code).


Module Boundary Map (folder ownership)

Each module agent owns only the folders listed. Never edit another module's folder. Shared primitives/types/store are owned by the Foundation and should be imported, not modified (extend via new files if truly needed, and document it).

Owner Owns (create/edit here) Reads (import only)
Foundation (this) src/components/ui/**, src/components/shell/**, src/lib/**, src/app/layout.tsx, src/app/globals.css, src/app/settings/**, tailwind.config.ts, next.config.ts
Competency src/app/matrix/** @/components/ui, @/lib/store, @/lib/types, @/lib/seed (COMPETENCY_SEED)
Module A (Daily Execution + Exam) src/app/tasks/**, src/app/exams/** @/components/ui, @/lib/store, @/lib/types, Task/Course seeds
Module B (GitHub Sync) src/app/sync/** @/components/ui, @/lib/store (useSettings), @/lib/types (Settings)
Module C (Metrics + Calendar) src/app/metrics/** @/components/ui, recharts, @/lib/store, @/lib/types (MetricPoint/Contest)
Module D (OpenRouter LLM) src/app/llm/** @/components/ui, @/lib/store (useSettings), @/lib/types (Settings)
Wave 3 (Dashboard) src/app/page.tsx everything (read-only wiring)

Adding a route to the nav: register it in src/components/shell/nav.ts (NAV_ITEMS). The sidebar renders from that list with active spotlight styling.

Replacing a stub: each module route currently renders <ModulePlaceholder module="..." owner="..." />. Replace the entire page.tsx.


Design Tokens ("Spider-Man Noir")

Canonical source: the @theme block in src/app/globals.css (Tailwind v4). It generates both CSS custom properties (var(--color-*)) and utility classes. tailwind.config.ts mirrors the palette for content globs + at-a-glance reference (wired via @config).

Surfacesnoir-bg #0a0a0b · noir-panel #131315 · noir-raised #1b1b1e · noir-hover #242427 Bordersrule-hairline #2c2c30 · rule-strong #3d3d42 Ink (aged paper)ink-primary #e9e6dd · ink-secondary #9a9890 · ink-muted #67655e Accentspotlight #f5f3ec (active/focus) + soft white glow Radiusrounded-deco (2px). Micro-padding px-2 py-1, hairline borders.

Utility classes: bg-noir-panel, text-ink-primary, border-rule-strong, bg-spotlight, font-display / font-sans / font-mono, plus custom: deco-rule (double-rule deco border), spotlight-active (glow), hatch-fill (diagonal half-tone), .film-grain / .vignette (decorative overlays — wrap a relative element; both are pointer-events-none).

Type — display: Cinzel (deco serif) · sans: Space Grotesk (grotesque) · mono: JetBrains Mono. All via next/font (variables in layout.tsx).

State badges (grayscale): not_started faint outline · in_progress half-tone hatch · production_verified solid inverted "stamped" chip. Use <Badge status={status} />.


Shared UI Primitives (@/components/ui)

All composable, fully typed, < 150 lines each. Import from the barrel.

Component Key props
Panel title?, actions?, bodyClassName?, children
Card title?, interactive?, children
Badge status?: Status | variant?, children?
Button variant?: "primary"|"secondary"|"ghost", size?: "sm"|"md", native button props
IconButton label: string (required), active?, native button props
Tabs items: TabItem[], activeId, onChange(id)
Table<T> rows, columns: Column<T>[], rowKey(row), onRowClick?, activeKey?, empty?
Modal open, onClose(), title?, footer?, children (Esc closes, scroll-locks)
ToastProvider / useToast useToast().push({ message, tone?, durationMs? })
ProgressBar value, max?, label?
SplitPane left, right, initialLeftPercent?, minPercent?, maxPercent?
EmptyState title?, description?, icon?, action? (noir flavor)
DecoFrame children (art-deco corner brackets)
FilmGrain / Vignette children (wrap a positioned container)
SpiderMark size?, title? (grayscale web-line emblem logo)

Shell helpers (@/components/shell): AppShell, Sidebar, TopBar, ModulePlaceholder, NAV_ITEMS.


Storage API (@/lib/store)

Typed repository over IndexedDB (idb-keyval) with a localStorage fallback. Immutable: inputs are never mutated; reads return fresh copies.

import { createStore, createSingletonStore, useStore, useSettings, STORE_KEYS } from "@/lib/store";

// Collection repo (entities must have an `id`)
const tasks = createStore<Task>(STORE_KEYS.tasks);
await tasks.findAll();           // T[]
await tasks.get(id);             // T | null
await tasks.upsert(entity);      // T[]  (insert or replace by id)
await tasks.remove(id);          // T[]
await tasks.replaceAll(list);    // T[]

// React hook: hydrate on mount, persist on change, expose loading
const { items, loading, upsert, remove, replaceAll } = useStore<Task>(
  STORE_KEYS.tasks,
  TASK_SEED, // optional seed written once if storage is empty
);

// Settings singleton (IndexedDB only — secrets never hardcoded)
const { settings, loading, save } = useSettings();

Store keys (STORE_KEYS, never hardcode): competency, tasks, courses, contests, metrics, settings.

Seeds (@/lib/seed): COMPETENCY_SEED (real Java + Python matrices, status not_started). TASK_SEED / COURSE_SEED / CONTEST_SEED / METRIC_SEED are intentionally empty — show a noir EmptyState until the user adds records.

Domain types (@/lib/types) — every type ships a Zod schema; the TS type is inferred from it so the two never drift. Validate at boundaries with xSchema.parse(...).

  • CompetencyNode { id, matrix, category, title, detail, status, updatedAt }
  • Task { id, title, problemUrl, complexityTag, estMinutes, priority, status, focusDate, scratchpad, createdAt }
  • Course { id, name, examDate, syllabusUrl, notes }
  • Contest { id, platform, name, startsAt, registrationDeadline, url, tier }
  • MetricPoint { date, platform, solved }
  • Settings { githubPat?, githubOwner?, githubRepo?, githubPath?, openRouterKey?, openRouterModel? }

Unions: Status = "not_started" | "in_progress" | "production_verified" · Matrix = "java" | "python" · Priority = "low" | "med" | "high" · MetricPlatform = "codeforces" | "leetcode" | "codechef".

Helpers: createId() (@/lib/id), cn(...) class combiner (@/lib/cn).

About

Aether Command: local-first Next.js developer command center with a high-contrast Spider-Man Noir design system.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages