Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 61 additions & 59 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,74 +1,76 @@
# CLAUDE.md — Smart-Mirror

## Context Loading
Read these Notion pages before starting work (use Notion MCP):
- **Core Context:** `32c062d6840b81388422e7452c1c5437` — who the user is, interaction style, active goals
- **Smart Mirror Project Doc:** `32c062d6840b8105a80ad51d36bf6e92` — full architecture, decisions, open items
- **Handoff Notes:** `32c062d6840b81deb1b0f1037da6e57a` — where we left off

## What This Is
Svelte SPA kiosk on a Raspberry Pi (rasplient, 100.66.29.15), cage/wayland kiosk mode. No mouse, no scroll. Compiles to static `dist/`, served by the mirror Blueprint in `eip-platform`. Frontend never talks to Notion directly — all data comes from one Flask polling endpoint.
Svelte/Vite kiosk UI for a Raspberry Pi mirror host.

## Build & Deploy
```bash
cd ~/Smart-Mirror && npx vite build
sudo kill -9 $(pgrep cage) # autologin respawns kiosk
# or: sudo systemctl restart eip-platform
```
Base path: `/mirror/` (vite.config.js). API: `/mirror/api` (src/lib/api.js).
The app compiles to static `dist/`, served by `eip-platform` at `/mirror/`.
The browser kiosk is launched from the Pi login shell (`~/.profile`)
with cage + Chromium pointed at `http://localhost:5000/mirror/`.

## Current Architecture

## Pages (5, arrow keys cycle, 2-min inactivity → Home)
- `src/App.svelte` owns the shell: background, topbar, active panel, notes/status
strip, keyboard navigation, and inactivity return-to-home.
- Page components are bounded panels. They do not own global fixed chrome,
full-screen overlays, or app-level z-index.
- `src/lib/store.js` polls `/mirror/api/data` every 15 seconds.
- `src/lib/calendar.js` owns local-date-safe calendar helpers.
- Runtime data is local Pi JSON, not Notion/Canvas/iCal.

| # | File | Content |
|---|------|---------|
| 0 | `Home.svelte` | Weather + daily quote. Clock rendered by App.svelte, not this component. |
| 1 | `Goals.svelte` | Daily/Weekly/Monthly Notion goals + Mirror Notes sticky note. |
| 2 | `DailySchedule.svelte` | Hour-by-hour grid (7 AM–10 PM), events as absolute-positioned blocks. |
| 3 | `WeeklySchedule.svelte` | 7-column week grid, recurring classes filtered by `days` property. |
| 4 | `MonthlyCalendar.svelte` | Calendar grid (left 2/3) + day detail panel (right 1/3). Starts from first Monday of month. |
## Data Contract

## Key Design Decisions
Frontend expects:

**Persistent clock in App.svelte** — single `AnimatedClock` instance, never unmounts. Positioned entirely via CSS `transform` so the browser animates position + scale in one transition:
```json
{
"goals": [],
"calendar": [],
"mirror_notes": [],
"last_updated": null,
"error": null
}
```
Home: transform: translate(50vw, 30vh) translateX(-50%)
Corner: transform: translate(calc(100vw - 2rem), 1.5rem) scale(0.22) translateX(-100%)

Backend source path on Pi:

```text
~/mirror-data/mirror.json
```
Why: avoids remounting (no flash), single source of truth for time display.

**Single data store** — `src/lib/store.js` polls `/mirror/api/data` every 60s. All pages read from `$mirrorData`. No component fetches independently.
Data-only updates should push JSON to that file. Code/UI updates rebuild and
copy `dist/`.

## File Map
## Files

### `src/components/ui/` — shared widgets
| File | What / Why |
|------|-----------|
| `AnimatedClock.svelte` | Time + date with minute-tick fade animation. Props: `align`, `timeSize`, `dateSize`, `showDate`. Only instantiated once in App.svelte. |
| `StickyNote.svelte` | Post-it style note block. Caveat handwriting font, yellow background, sharp corners, tape strip. Props: `notes: string[]`, `max: number`. |
|------|------------|
| `src/App.svelte` | Persistent shell + panel routing |
| `src/components/Home.svelte` | Large ambient clock, weather, quote, quick next/notes |
| `src/components/Goals.svelte` | Focus board grouped by horizon |
| `src/components/DailySchedule.svelte` | Today timeline |
| `src/components/WeeklySchedule.svelte` | Seven-day card grid |
| `src/components/MonthlyCalendar.svelte` | Month grid + selected-day detail |
| `src/components/ui/AnimatedClock.svelte` | Shared clock/date renderer |
| `src/lib/store.js` | Single API polling store |
| `src/lib/calendar.js` | Date/event helpers |

### `src/lib/` — utilities
| File | What / Why |
|------|-----------|
| `api.js` | `API_BASE` — one place to change if platform prefix moves |
| `store.js` | `mirrorData` writable store + polling loop |
| `calendar.js` | `eventsOnDate()`, `fmtTime()`, `fmtHour()`, `WEEK_ORDER` — all schedule filtering |
| `time.js` | `time` readable store (1s tick), `formatTime`, `formatDateLong` |
| `navigation.js` | Arrow key listener + inactivity tracker |
| `weather.js` | Open-Meteo fetch, 30-min cache. Coords: Livingston NJ (40.79, -74.32) |
| `quotes.js` | 30 stoic quotes, date-seeded (rotates daily) |

## Conventions
- Dark theme: bg `#000`, text `#e0e0e0`, accent `#4fc3f7`
- All font sizes via `clamp()` — no hardcoded px
- `tabular-nums` on clock elements (prevents layout shift)
- No CSS frameworks, no router library
- Page components own content only — App.svelte owns chrome (clock, dots, transitions)
- Commit: short, imperative

## Do Not Touch
`.profile`, kiosk boot flow, nocursor.so, systemd services, cage config.

## Working Style
- User skims — short answers, exact commands
- Ask before destructive changes
- After sessions: update Handoff Notes + Smart Mirror project doc in Notion
## Design Rules

- Dark ambient dashboard; no scroll, no visible cursor.
- One shell owns page transitions; page components stay inside their panel box.
- Avoid overlay stacks that transform whole pages over one another.
- Keep kiosk-friendly text large, sparse, and glanceable.
- Use local dates (`dateKey`) instead of UTC slicing for today/week/month logic.

## Commands

```bash
npm install
npm run build
```

Deploy built assets to:

```text
~/Smart-Mirror/dist
```
53 changes: 41 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,61 @@
# Smart Mirror

Minimal Svelte + Vite smart-mirror UI with home and dashboard screens.
Svelte/Vite kiosk UI for the Raspberry Pi mirror.

## Quickstart
The Pi serves the built frontend from `~/Smart-Mirror/dist` through the
`eip-platform` Flask app at `/mirror/`.

## Run locally

```bash
npm install
npm run dev
```

`npm run dev` starts the app at http://localhost:5173.
The dev server runs at `http://localhost:5173`.

For a production build:
## Build

```bash
npm run build
npm run preview
```

## Routes
The production build is written to `dist/` with Vite base path `/mirror/`.

## Data contract

The frontend polls:

```text
/mirror/api/data
```

Expected shape:

```json
{
"goals": [],
"calendar": [],
"mirror_notes": [],
"last_updated": null,
"error": null
}
```

Runtime data lives on the Pi at:

```text
~/mirror-data/mirror.json
```

- `/` – Home screen showing centered date and time.
- `/dashboard` – Dashboard with date top-center, time top-right, and Daily/Weekly/Monthly sections.
Update mirror content by copying a new JSON file to that path. No Notion,
Canvas, iCal, or frontend rebuild is required for data-only updates.

## Navigation

- Clicking anywhere on the Home screen takes you to the dashboard.
- The dashboard monitors mouse movement and clicks; after two minutes of inactivity it automatically returns to the home screen.
- Any activity resets the timer.
- Arrow right/down: next panel
- Arrow left/up: previous panel
- `R`: refresh local mirror data immediately
- Two minutes of inactivity: return to Home

Time and date update every second via a Svelte store.
Panels: Home, Focus, Today, Week, Month.
Loading