Skip to content

Latest commit

 

History

History
146 lines (108 loc) · 6.66 KB

File metadata and controls

146 lines (108 loc) · 6.66 KB

Summit — Architecture Document

Note: This is a planning artifact generated by /peak-workflow:setup. Sections marked with (to be completed during implementation) will be populated as epics are implemented. Run /peak-workflow:refresh-docs after completing epics to update this document to reflect the as-built system.


1. System Overview

Summit is a single-page habit tracker: users add habits, mark them done for the day, track a streak count per habit, and filter by active/archived status. It is a client-only SPA with no backend — all state is persisted in the browser's localStorage. It is built as the public reference example for the peak-workflow plugin.


2. Tech Stack

Layer Technology Purpose
Language TypeScript Application logic and type safety
Build tool / dev server Vite Local dev server, bundling, build-time constants
Package manager npm Dependency management
Persistence localStorage Client-side storage of habits and completion state
Styling PeakFlames Design System (vendored token CSS + .pf-* classes) Brand canvas, type ramp, and component visual treatment

3. Data Sources

No external data sources or APIs. All application state lives in the browser's localStorage — no database, no network calls.


4. API Design

N/A — this is a client-only SPA with no backend or API surface.


5. Backend Architecture

N/A — there is no backend.


6. Frontend Architecture

Single-page app with no router — one screen, rendered imperatively with the DOM API (no framework). Component hierarchy (decomposed across Epics Yz4JE9Z, WKhBuVK, and e3mj8uq):

Stylesheets

src/styles/peakflames/ holds the PeakFlames Design System's token CSS, vendored byte-identical from the design project (styles.css plus 10 tokens/*.css files — see design-notes.md §14). src/styles/main.css imports it as its first line, then layers Summit-specific layout (flex containers, gaps, list-row padding) and a small number of state overrides the vendored .pf-* classes can't express (the amber, not flame, streak value; a desaturated done-button treatment). Components add .pf-* classes alongside their existing class names — see Key Components in docs/implementation-plan/phase-3-frontend/epic-R5e7z3Y-*.

src/main.ts              — bootstrap: emits the startup console.info line, then mounts the app
  src/App.ts               — mountApp(root) + render(root): composition root
    renderAddHabitForm()    — src/components/AddHabitForm.ts
    renderFilterToggle()    — src/components/FilterToggle.ts
    renderStreakHint()      — src/components/StreakHint.ts (shown only in Active view)
    renderHabitList()       — src/components/HabitList.ts
      renderHabitCard()       — src/components/HabitCard.ts (per habit)
        renderStreakBadge()     — src/components/StreakBadge.ts (displays streak count)
      renderEmptyState()      — src/components/EmptyState.ts (when no habits in view)
    renderFooter()         — src/components/Footer.ts

State Management

State is split across four layers:

  • Persistence: src/storage/habitStore.ts and src/storage/streakRecalculation.ts
    • loadHabits() / saveHabits() against the namespaced summit.habits key in localStorage, holding the full dataset as a single JSON array
    • todayISO() (local-date YYYY-MM-DD, not UTC) and recalculateAll() for load-time streak staleness recalculation
  • Domain models: src/models/Habit.ts — the Habit type: { name, streak, lastCompletedDate, archived }
  • Mutation layer: src/state/habitActions.ts
    • addHabit(habits, name) — inserts a new habit, calls saveHabits()
    • archiveHabit(habits, target) / unarchiveHabit(habits, target) — mutate archived state, call saveHabits()
    • markDone(habits, target, today) — applies streak arithmetic via markDoneStreak(), persists via saveHabits()
  • Streak logic: src/state/streakLogic.ts
    • markDoneStreak(habit, today) — pure function implementing increment/no-op/reset rules: returns no-op if already done today, increments if completed yesterday, resets to 1 otherwise
  • View state: src/state/viewState.ts
    • createViewState() — encapsulates the current filter view (Active / Archived)
    • filterHabits(habits, view) — filters the habit list by view

mountApp loads and recalculates on every mount, then persists the recalculated result back (so a staleness reset survives the session) before the first render. Every subsequent mutation (add, done-today toggle, archive/unarchive) calls saveHabits() synchronously before re-rendering, guaranteeing the write completes before the next user action is possible.

The app version is a compile-time constant (__APP_VERSION__), injected by Vite's define in vite.config.ts from package.json's version field — not imported into src/ at runtime — per CLAUDE.md's "Version single source of truth" convention. The same vite.config.ts config also configures Vitest (test.environment: 'jsdom', setupFiles: ['./tests/setup.ts']), so __APP_VERSION__ is substituted in unit tests too.

Test environment note: Node 22+'s experimental native localStorage global shadows jsdom's working implementation, leaving localStorage non-functional in Vitest tests. tests/setup.ts works around this by installing a minimal in-memory MemoryStorage polyfill (implements Storage) onto globalThis before tests run — the app itself touches only the real browser localStorage API.


7. Background Services

N/A — no background jobs, scheduled tasks, or hosted services.


8. Container / Infrastructure

npm run build runs tsc --noEmit for type checking, then vite build, producing a static dist/ directory (HTML, JS, CSS) with no server-side component. npm run preview serves that dist/ output locally for a production-like smoke check.

Summit deploys to GitHub Pages (project page at https://peakflames.github.io/summit/). .github/workflows/deploy-pages.yml runs lint, tests, and the build on every push to main, then publishes dist/ via actions/upload-pages-artifact + actions/deploy-pages. Because Pages serves project pages from /<repo>/ rather than /, the build sets base: '/summit/' in vite.config.ts — gated behind a GH_PAGES=true env var the workflow sets, so local dev/build/preview are unaffected. See CLAUDE.md's Deployment section for details.


9. Security & Access

No authentication or authorization — single-user, client-only application with no server-side data. No secrets are expected in this codebase (see CLAUDE.md's Security Baseline).