Skip to content
Merged
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,6 @@ graphify-out/

# Retrieval model files (rag/embed.py MODEL_CACHE), fetched at build time
backend/.models/
.superpowers/
frontend/test-results/
frontend/playwright-report/
13 changes: 13 additions & 0 deletions docs/CODEBASE-GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,19 @@ embeds the demo documents once per server and uploads the files in the backgroun
`globals.css`). `components/dashboard-shell.tsx` is just a page's content wrapper and optional header card.
- There is no root `loading.tsx`: the pages are static and prefetched, and a full-screen spinner made every click
look like a reload.
- **Sky theme (UI revamp, `docs/superpowers/specs/2026-10-20-ui-revamp-design.md`).** `app/(app)/layout.tsx` wraps
the shell in `<div data-app-theme="sky" className="contents">`. In `globals.css`, `:root:has([data-app-theme="sky"])`
swaps every shadcn colour token to the light Sky palette, so portaled menus and dialogs match too. The
`dark:` variant is also switched off while the shell is mounted. Public pages keep the forced dark theme.
Extra tokens: `good`, `bad`, `accent-soft`, `--band`. Type roles: `font-serif` (Newsreader), `font-label` /
`.label-mono` (IBM Plex Mono labels).
- **No layout shift from overlays.** `globals.css` sets `scrollbar-gutter: stable` and cancels Radix's scroll-lock
margin (`body[data-scroll-locked]`), so opening a menu or dialog never moves the page. Guarded by
`npm run test:e2e` (Playwright, local only), which drives the dev-only harness `app/dev/layout-shift` (404 in
production). Firefox is opt-in with `E2E_FIREFOX=1`.
- **Motion:** use `motion/react` only (framer-motion was removed). Shared timings live in `lib/motion.ts`
(`DURATION`, `EASE_OUT`, `fadeUp`). Reduced motion is honoured globally by `MotionConfig reducedMotion="user"`
in the app layout and a CSS rule in `globals.css`.

## 7c. Ask Lumen screen (frontend, UI phase)
- `/chatbot` (`app/chatbot/chatbotContent.tsx`) is the one chat with the agent (the old `/agent` test page is gone).
Expand Down
15 changes: 15 additions & 0 deletions docs/direction/STORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -311,3 +311,18 @@ with the numbers and the files that prove them. Newest last. Raw numbers live in
the old regime's slabs ("first ₹2.5 lakh at 0%"). Fixing that, and L05 (unused subscriptions) and L08 (savings
goal), needs a prompt/tool change and a new recording. Story point: a grader bug can hide a dishonest answer
as easily as it can fail a right one.

## 27. Stopping overlays from nudging the page (UI revamp, Phase 0)
- **S:** Opening the profile menu shifted the Ask Lumen chat column sideways. Every menu, dialog, select and sheet in
the app did the same, because Radix's scroll lock adds a margin equal to the scrollbar width.
- **T:** Fix it once for the whole app, prove it stays fixed, and lay the light "Sky" theme over the signed-in app
without touching the dark public pages.
- **A:** A dev-only harness page with one of each overlay and a Playwright check that measures the content column
before and after opening each one. The headless browser's hidden scrollbars first masked the bug, so the check
turns them back on. The fix is two CSS rules: a stable scrollbar gutter and cancelling the lock's margin. Sky tokens
are keyed on `:root:has([data-app-theme="sky"])`, so portaled menus match and the `dark:` variant switches off
in-app. One animation library instead of two, with reduced motion honoured globally.
- **R:** Shift dropped from 7.5 px to 0 on 4 of the 5 overlays (the fifth, non-modal, was already fine).
12 e2e checks pass, including portal colours, AA contrast for muted text (4.9:1), login staying dark and
reduced motion. framer-motion was removed. Story point: reproduce in a test first. Here the test environment
was hiding the very bug.
342 changes: 342 additions & 0 deletions docs/superpowers/plans/2026-10-20-ui-revamp-phase0.md

Large diffs are not rendered by default.

205 changes: 205 additions & 0 deletions docs/superpowers/specs/2026-10-20-ui-revamp-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
# Lumen inner-app UI/UX revamp: design

Status: **DRAFT for owner review** · 2026-10-20 · supersedes the "no new design system" line in
`docs/direction/specs/SPEC-UX.md` (its evidence-first chat and review-queue goals still stand).
Reference mockup: [`2026-10-20-ui-revamp/home-mockup.html`](2026-10-20-ui-revamp/home-mockup.html)
(screenshot: `home-mockup.png`). Use the **Sky** variant; the Ink variant is rejected.

## 1. Why

The signed-in app still carries its November 2025 skeleton: a stock shadcn sidebar ("Financial command
center", "Workspace", "Compare"), tabs that no longer match what the app does (Analytics and Spending Views
open the same page, "Review (test)", a separate Import page), and an Overview that looks like every
AI-generated dashboard. The owner wants a full rethink (information architecture, layout, palette, motion),
not a re-skin. Inspiration: Origin Financial's app (useorigin.com): dense, warm, every card says something
specific, serif + mono-label typography, a sky motif that our landing page already uses.

Success = opening Lumen feels like a calm, specific briefing on *your* money, not a template; nothing on screen
moves unless you asked it to; and it stays light on the free Vercel tier.

## 2. Goals / non-goals / anti-goals

**Goals**
- New information architecture and sidebar (section 3).
- A new Home built from the mockup (section 4).
- A design system: Sky palette, type scale, spacing, motion rules (sections 5–6), applied to every inner page.
- Ask Lumen touch-up: fresh chat on open + history list; Home Ask bar hands off smoothly (section 7).
- A **zero layout-shift rule** enforced app-wide, starting with the known bug (section 6.3).
- Frontend performance pass (section 9).

**Non-goals**
- Bank/card linking, net worth, credit score, investments, financial-planner booking, paywalls. Every number
comes from imported statements, receipts and invoices already in Lumen.
- Landing page, login and onboarding (already redesigned; only re-tint to share tokens if trivial).
- The BYOK feature itself (another session builds it). This revamp only gives it a home: the avatar menu.

**Anti-goals** (fail even if it works)
- Generic KPI-card grid, gradient-blob "AI dashboard" look.
- Any click on one region visibly nudging another region (the profile-menu bug class).
- Invented numbers or scores. A card with no real data shows an honest empty state, not a placeholder figure.
- Bouncy or slow animation; anything that ignores `prefers-reduced-motion`.

## 3. Information architecture (owner-decided)

```
Lumen
Home
TRACK
Spending ← merges /analytics (+weekly/monthly/yearly tabs) and /ai-analytics charts
Bills & subscriptions ← recurring charges, payment calendar, reminders
PLAN
Budget (NEW)
Forecast (later; shown only once built)
───────────
Receipts & statements 3 ← merges /documents, /import, /review; badge = items needing review
Ask Lumen ⌘K
```

- Group labels describe what the user does (owner rule: never name groups after backend concepts like
"AI" or "Data"). **Insights is not a tab**: its content becomes Home's "Lumen found" cards and the Spending
page.
- **Profile, settings, API keys (BYOK), theme, log out → top-right avatar menu.** The sidebar is navigation
only.
- **Collapse:** icon rail (~56px) with tooltips, toggled by a button and Ctrl/⌘+B, remembered across visits
(the existing shadcn sidebar cookie is fine). The content area animates width; content must not jump.
- Mobile (<768px): the sidebar becomes a sheet (existing shadcn behaviour). Builder's call on adding a
bottom tab bar.
- Old URLs (`/dashboard`, `/analytics`, `/ai-analytics`, `/chatbot`, `/documents`, `/import`, `/review`,
`/upload`) redirect to their new homes so bookmarks and the demo keep working. New route names are the
builder's call (suggested: `/home`, `/spending`, `/bills`, `/budget`, `/receipts`, `/ask`).
Until Phase 5 redesigns them, the new URLs render the existing page content (Spending = analytics +
ai-analytics stacked; Receipts = documents + import + review as tabs). Budget's nav item appears only once
its page exists (my call).

## 4. Home (owner-approved content)

Layout per the mockup: greeting row (left) + icon buttons and avatar (right); sub-tabs
**Overview · This month · Year**; a one- or two-sentence brief in Newsreader; then a two-column grid
(main ~1fr, side ~330px) over a sky-tinted header band that fades into the page.

| Card | Content | Data |
|---|---|---|
| Brief | e.g. "October is running 9% lighter than September. The one thing worth a look: food delivery is up ₹2,140, and Netflix renews on Friday." | **Rule-based, no LLM per page load.** New backend: a summary endpoint picks the top fact or two from month-to-date vs last month, the biggest category change, and the next bill. |
| Spent this month | Cumulative spend line for this month vs last month (dashed) plus a dotted forecast to month-end, with bill pins on the future part. Range chips 1W/1M/3M/YTD. | Existing `/api/analytics/dashboard` + `/forecast`; recurring bills for the pins. |
| Lumen found | "N things worth ₹X this month", then up to 3 action cards: mono TAG, serif number-led headline, one line of why, a link. Kinds: unused/new subscriptions, category spikes, receipts needing review, duplicate charges. | New aggregation endpoint over the existing anomalies, insights and recurring detection and the review queue. ₹ "worth" only where a real saving exists; otherwise the headline carries no amount. |
| Spending calendar + Recent transactions | Month heat-grid (darker = more spent, amount in cell) beside the last 5 transactions. | Existing invoice/transaction data. |
| Budget **(NEW)** | "₹spent of ₹budget", progress bar, "₹left · N days · ~₹/day". Empty state → the draft-budget card. | New budget model + API (section 8). |
| Let Lumen draft your budget **(NEW)** | Promo card; drafts per-category limits from 3 months of spending; user approves each line before save (Lumen's propose-then-approve rule). | Reuses the agent proposal/approve flow. |
| Upcoming bills | Next 3 recurring charges with date chips. | Existing recurring detection / reminders. |
| Activity | Imports, receipts read, new subscriptions spotted, with relative times. | New lightweight feed from created_at timestamps; no new event table unless needed. |

Deferred: the "+" ask-about-this button on every card (owner: maybe later). The Ask bar lives in the sidebar
("Ask anything ⌘K"); typing there and pressing Enter routes to Ask Lumen with the question already
running (section 7).

## 5. Visual system: Sky (owner-decided)

Tokens from the mockup, to be re-expressed as `oklch` CSS variables in `globals.css` (replacing the current
blue-grey dark theme):

| Token | Value | Use |
|---|---|---|
| bg | `#f4f3ef` | page (warm paper) |
| side | `#efeee9` | sidebar |
| card | `#ffffff` | cards |
| line | `#e6e4de` | borders, dividers |
| text / mute | `#1b1b1a` / `#7c7a74` | body / secondary |
| accent / accentSoft | `#2f6fe0` / `#dfe9fb` | links, chart line, selection / promo cards |
| good / bad | `#2e8b57` / `#c4572f` | deltas, bill pins |
| band | `linear-gradient(#cfe2fb → #e9f1fb → bg)` | sky header band, Home only (builder may reuse on other page headers) |

Light theme only for this revamp, scoped to the signed-in `(app)` route group; the root layout's forced
`dark` stays for landing/login/onboarding (my call). No "theme" item in the avatar menu yet. Ink (dark) is out; a later dark mode is possible because everything goes
through tokens.

Type: **Funnel Sans** (UI, numbers), **Newsreader** (brief, card headlines, italics for emphasis),
**IBM Plex Mono** small caps-style labels (10.5px, 0.12em tracking) for card titles, tags and axis ticks.
Load fonts via `next/font` (self-hosted, no layout shift). Radius 14px for cards, 8px for controls.

## 6. Interaction and motion (owner-decided: quiet and quick)

### 6.1 Motion rules
- 150–250ms, ease-out; opacity/transform only (never animate layout properties except the sidebar width).
- Route change: content fades/slides 4–8px; the sidebar and header never re-animate.
- Numbers count up once on first load, not on every revisit.
- Ask handoff: the sidebar Ask input morphs into the chat composer (shared element / View Transitions API
where supported; plain fade fallback).
- All motion off under `prefers-reduced-motion`.
- Use **one** animation library (`motion`); drop the duplicate `framer-motion` import path.

### 6.2 Feedback states
Every async card has a skeleton matching its final size (no jump when data arrives), an empty state with a
next step, and an inline error with retry. Abstaining is a designed state (from SPEC-UX).

### 6.3 Zero layout-shift rule (owner requirement)
**Known bug:** opening the profile dropdown shifts the chat column left. Root cause: Radix menus/dialogs are
modal by default; `react-remove-scroll` hides the page scrollbar and adds compensating padding, so centered
content moves. This affects every DropdownMenu, Dialog, Select and Popover.
**Fix once, globally:** `html { scrollbar-gutter: stable; }` plus neutralising the scroll-lock padding
(`body[data-scroll-locked] { margin-right: 0 !important; padding-right: 0 !important; }`), and/or
`modal={false}` on non-blocking menus in the shared `ui/` wrappers.
**Rule going forward:** an interaction in one region may not change the size or position of another region.
Overlays float; reserved space for async content; images and fonts sized ahead of time. Verify with a
Playwright check that opens each menu/dialog and asserts that main-content bounding boxes don't change (CLS = 0).
Playwright is a dev-only dependency; the check runs locally against the dev server with demo data and is
**not** a CI gate for now (my call; promote to CI once a seeded signed-in CI app exists).

## 7. Ask Lumen touch-up (owner-decided)

- Opening Ask Lumen starts a **fresh chat** with suggested questions (drawn from the current "Lumen found"
items); past chats are in a collapsible history list on the side; one click resumes a chat.
- **Real threads (owner-decided):** add a conversation/thread id to `ChatMessage` (migration) plus endpoints
to list threads, create one, and load one. Existing flat history becomes a single "Earlier chats" thread.
This makes Phase 4 full-stack.
- From the sidebar Ask bar or ⌘K: Enter → navigate to Ask with the question already sent, so the answer
appears as the page settles (Google AI-Overview feel). **No streaming in this revamp (owner-decided):**
show a polished thinking state (work-trace steps animating in), then fade in the full answer. Token
streaming (SSE) is a separate follow-up task.
- Restyle to Sky; keep evidence-first behaviour (citations, work trace, proposal cards) from SPEC-UX.
- Fix the stray "Next:" artefact visible under answers (seen in the 2026-10-20 screenshot).

## 8. New backend this implies

1. **Home summary** endpoint: the brief sentences + "Lumen found" items, rule-based, cached per user per day
or invalidated on import.
2. **Budget**: per-user monthly budget with per-category limits (table + CRUD); "draft budget" as an agent
proposal the user approves.
3. **Activity feed**: recent events from existing timestamps.
4. **Chat threads** (section 7).

Each ships with tests in the existing mocked-LLM suite style.

Calls made while writing this (not owner-decided; owner may override):
- New endpoints go in FastAPI `backend/api/` (the newer pattern, where agent proposals live), reading the
analytics helpers as functions rather than calling the Flask routes.
- Draft budget = **one proposal per category** using a new `set_budget` proposal type, so the existing
approve/reject-per-proposal flow and card UI work unchanged. "Approve all" is a convenience button. Fits the AI-engineering story: the draft
budget is tool-calling + human approval.

## 9. Performance pass (last phase)

- Remove unused heavy dependencies from the app bundle: `three`, `postprocessing`, `lenis` (check landing-page
use first; lazy-load them there if still needed), `mathjs` if unused, the duplicate `framer-motion`.
- Code-split charts (`recharts`) with `next/dynamic`; server-render static shells where possible.
- Measure before/after (bundle size per route, Lighthouse LCP/CLS/INP) and log the numbers as a STAR entry
in `docs/direction/STORY.md`.

## 10. Phased path (one PR per phase into `refactor`)

| # | Phase | Contents |
|---|---|---|
| 0 | Foundations | Sky tokens, fonts via next/font, global layout-shift fix + Playwright CLS check, motion rules |
| 1 | App shell | New sidebar IA, icon-rail collapse, top-right avatar menu (settings / BYOK slot / log out), route redirects |
| 2 | Home UI | Mockup → components with real existing data; budget/brief/activity cards render empty states |
| 3 | Home backend | Summary + "Lumen found" endpoint, activity feed, budget model + draft-budget proposal; wire into Home |
| 4 | Ask Lumen | Chat threads (migration + endpoints), fresh chat + history list, Ask-bar handoff with thinking state, restyle, "Next:" fix |
| 5 | Inner pages | Spending, Bills & subscriptions, Budget, Receipts & statements: each gets a short mockup round with the owner before build |
| 6 | Performance | Section 9, with measured before/after |

Keep `docs/CODEBASE-GUIDE.md` current in each PR.

## 11. Left to the builder
- Exact route names, component file layout, and whether Home sub-tabs "This month" / "Year" ship in phase 2 or later.
- Chart implementation (keep recharts or hand-rolled SVG like the mockup), as long as it matches the look and passes the perf budget.
- Mobile layout details beyond "sidebar becomes a sheet".
- Copy for empty states and the brief's rule templates.
Loading
Loading