diff --git a/AGENTS.md b/AGENTS.md index 66c7d9c..45c8803 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -107,11 +107,12 @@ Subsystem rules live in `docs/guides/` and lazy-load in Claude Code via `.claude ## Current state -_**Box live at v0.1.0:94**; `main` ahead by mobile Phases 0–7 + P3b + drag-reorder + **8a–8i — Phase 8 complete** — **all deploy-pending** (no s9pk built). **The fundraising grid + email capture is the canonical system of record.** Active thread: **mobile-first redesign — feature-complete; next is deploy + real-phone device-test** (nothing verified on a real phone yet). Build reference: `design/phase8-conformance.md` (the 8a–8i spec; now fully landed — archive-eligible). **Plan (Grant, 2026-06-19): finish features first → then Grant device-tests + deploys.** History: git log + `start9/0.4/startos/versions/`._ +_**Box live at v0.1.0:94**; `main` ahead by mobile Phases 0–7 + P3b + drag-reorder + **8a–8i — Phase 8 complete** + **installable PWA (Option A)** — **all deploy-pending** (no s9pk built). **The fundraising grid + email capture is the canonical system of record.** Active thread: **mobile-first redesign — feature-complete; next is deploy + real-phone device-test** (nothing verified on a real phone yet). Build reference: `design/phase8-conformance.md` (the 8a–8i spec; now fully landed — archive-eligible). **Plan (Grant, 2026-06-19): finish features first → then Grant device-tests + deploys.** History: git log + `start9/0.4/startos/versions/`._ - **Mobile redesign — 4 core surfaces built (Grid · Contacts · Pipeline · Reminders), each a rules-of-hooks-safe `useIsMobile()` → `Mobile*`/`Desktop*` pair (desktop untouched).** Foundation: bottom-tab bar + `:root` mobile vars; 4-stage enum; derived grid signals injected-on-GET/stripped-on-write at both points; mobile writes use **one-row endpoints only** (log-communication, pipeline link/stage, reminders, `update-row`) — never whole-grid PUT. - **Phase 8 done (8a–8i — complete)** — cards/details/sheets/shell re-authored to the dc anatomy. The durable per-primitive record is the **Design convention's primitives list**; per-phase "what changed" is in git log. Recent: **8h** Grid-detail loose ends (G4 tappable stage card, G5 dedicated Reminder card, Open-in-Grid deep-link on all three detail surfaces); **8i** shell — `BottomTabIcon` SVG line-icons replace the bottom-tab emoji glyphs + the `·Ten31·` `.mobile-wordmark` in the top bar (page title now `desktop-only`); the dc top bar's quick-log/theme/account cluster already existed. jsdom-verified (throwaway 375px harness). - **Live (deployed):** W2 NL query (v94); W1 reminders (v93); grid Pipeline (v88); Matrix intake + Gmail capture (DWD) + daily digest; Thesis/Architect (dual-approval); outreach — all draft-only. - **Tests:** **40/40 backend green** (`python3 backend/run_tests.py`), `py_compile` clean. Mobile surfaces interaction-verified via throwaway 375px jsdom harnesses (deleted after); harness recipe + the `Simulate.change` input gotcha live in the `mobile-surface-verification` memory. -- **Next — feature-complete; deploy + device-test:** Phase 8 is fully landed (8i closed S1/S2; **Pipeline accordion skipped** per Grant). Remaining is **deploy P0–P8 + P3b in one s9pk** (**authorize + version-bump first**, per `docs/guides/packaging.md`) and **device-test light/dark on a real phone** — nothing mobile has run on a real device yet (smoke/jsdom only). -- **Open / risks:** all mobile work + light theme **built but never deployed or device-tested** (smoke/jsdom only); `MobileDetailRow` unused-but-retained (legacy-usage sweep); Pipeline detail "Committed" tile shows grid-committed not deal-expected (forecast in a footnote); `handle_get_opportunity` (single-opp GET) deliberately does NOT inject `existing_investor`/`last_contact_date` — no surface needs it (the card uses the list injection; the detail derives `existing` from the contact fetch); W2 happy-path only; **Claude/Architect path unverified live on the box**; v2.0 reserve-asset spine **not canonical**; doc drift — `crm-overview.md`/`EVALUATION.md` still call `lp_profiles` live. +- **Installable PWA — BUILT 2026-06-20 (Option A, iPhone-first, no service worker):** `frontend/manifest.webmanifest` (standalone, `theme_color` `#0b1118`) + square icons (`ten31-app-icon.svg` → `icon-192`/`icon-512`/`apple-touch-icon`) + `
` manifest/`apple-mobile-web-app-*`/`viewport-fit=cover` metas + one pre-auth `/manifest.webmanifest` route (`application/manifest+json`). Adds to home screen → full-screen standalone, dark status bar. No SW by design (iOS A2HS needs none; avoids the stale-shell class). render-smoke + live-curl verified. Detail in `ROADMAP.md` "Mobile PWA". **Deploy:** rides the same s9pk. +- **Next — feature-complete; deploy + device-test:** Phase 8 is fully landed (8i closed S1/S2; **Pipeline accordion skipped** per Grant) + the installable PWA. Remaining is **deploy P0–P8 + P3b + PWA in one s9pk** (**authorize + version-bump first**, per `docs/guides/packaging.md`) and **device-test light/dark on a real phone** (incl. installing to the iOS home screen + standalone launch) — nothing mobile has run on a real device yet (smoke/jsdom only). +- **Open / risks:** all mobile work + light theme **built but never deployed or device-tested** (smoke/jsdom only); `MobileDetailRow` unused-but-retained (legacy-usage sweep); Pipeline detail "Committed" tile shows grid-committed not deal-expected (forecast in a footnote); `handle_get_opportunity` (single-opp GET) deliberately does NOT inject `existing_investor`/`last_contact_date` — no surface needs it (the card uses the list injection; the detail derives `existing` from the contact fetch); W2 happy-path only; **Claude/Architect path unverified live on the box**; v2.0 reserve-asset spine **not canonical**; doc drift — `crm-overview.md`/`EVALUATION.md` still call `lp_profiles` live; **PWA iOS status bar is fixed `black` at launch** (`apple-mobile-web-app-status-bar-style`) — in *light* theme it's a black strip above the light header (proper fix = `black-translucent` + header top-safe-area padding + theme-synced `theme-color`; deferred, validate on-device; dark is default so low-priority); PWA manifest/icons sent with no `Cache-Control` (consistent with all static routes — a manifest change post-install may be served stale by iOS until it re-fetches). diff --git a/ROADMAP.md b/ROADMAP.md index 8203248..3ab31ce 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -280,6 +280,33 @@ backlog. **Scoped 2026-06-19 (plan below); not yet started.** The comps are signed-off prototypes, **not drop-in** (Claude Design runtime, seed data) — each surface is re-authored in the app's React idiom and wired to the **real API**.* +#### Mobile PWA — installable home-screen app — BUILT 2026-06-20 (deploy pending) + +**Option A (iPhone-first, no service worker).** Makes the app installable to the iOS home +screen and launch **standalone** (full-screen, no Safari chrome, dark themed status bar, +splash). Shipped: `frontend/manifest.webmanifest` (`display:standalone`, `start_url:/`, +`theme_color`/`background_color` = the brand base `#0b1118` already reserved for this in +`design/tokens.tokens.json`); square icons generated from `ten31-app-icon.svg` (full-bleed +`#0b1118` + white "T31", maskable-safe) → `icon-192.png`/`icon-512.png`/`apple-touch-icon.png` +(180); `` gains `rel=manifest`, `theme-color`, the `apple-mobile-web-app-*` metas +(status bar `black` — opaque, so content never slides under the notch), `apple-touch-icon`, +and `viewport-fit=cover` (so the tab bar's existing `env(safe-area-inset-bottom)` clears the +home indicator). One pre-auth backend route serves `/manifest.webmanifest` as +`application/manifest+json` (`backend/server.py`); icons serve via the existing `/assets/` +handler. **No service worker** — on iOS the install prompt doesn't exist regardless (A2HS is +always manual via Share), standalone display needs none, and a cache-first SW would reintroduce +the stale-shell class the render-smoke gate guards against. Verified: render-smoke green + +live-curl (manifest + icons 200 pre-auth, correct content-types). **Deploy:** ships in the +next s9pk with the mobile phases. +- **Known minor:** the iOS status bar is fixed `black` at launch (can't follow the in-app + light/dark toggle); a barely-perceptible seam vs the `#0b1118` app. Acceptable; dark is default. +- **Deferred (not needed for iPhone):** a network-first service worker → Android's "Install" + prompt + faster relaunches; the JSX-precompile build-step (ROADMAP below) is the better lever + if relaunch speed is ever a felt problem. +- **Adjacent issue (not PWA, noted while here):** a phone in **landscape** can exceed the 768px + breakpoint and render the *desktop* layout; `orientation:portrait` in the manifest hints at + this but iOS ignores it for home-screen apps. Revisit if it bites during device-testing. + #### Phase 8 — conform to the FINAL Claude Design mockups (mobile) — **NEXT SESSION (scoped 2026-06-19)** *Phases 0–7 built the mobile surfaces + light theme. Phase 8 closes the gap to the **final** design + functional parity. Two independent agent passes ran 2026-06-19 (functional-parity + visual-conformance); their findings + the source-of-truth correction below drive this plan.* diff --git a/backend/server.py b/backend/server.py index ea09fc0..9d9bf3e 100644 --- a/backend/server.py +++ b/backend/server.py @@ -2226,6 +2226,11 @@ class CRMHandler(BaseHTTPRequestHandler): # Serve frontend if path == '/' or path == '/index.html': return self.send_file(os.path.join(FRONTEND_DIR, 'index.html')) + # PWA manifest — served at root (manifest scope = its own path's directory) and + # pre-auth, since the browser fetches it (and the icons under /assets/) before login + # to offer "Add to Home Screen". No service worker (iOS-first; see ROADMAP). + if path == '/manifest.webmanifest': + return self.send_file(os.path.join(FRONTEND_DIR, 'manifest.webmanifest'), 'application/manifest+json') if path.startswith('/assets/'): filepath = os.path.join(FRONTEND_DIR, path.lstrip('/')) # Containment check: get_path()/urlparse does NOT normalize '..', so without diff --git a/design/tokens.tokens.json b/design/tokens.tokens.json index 39f1bec..2c1c476 100644 --- a/design/tokens.tokens.json +++ b/design/tokens.tokens.json @@ -2,7 +2,7 @@ "$description": "Ten31 CRM design tokens (W3C DTCG). Extracted as-built from frontend/index.html :root + an inline-style census, 2026-06-18. The app currently inlines these values (CSS :root vars + ~1300 inline style objects); this file is the canonical source going forward. Some real values (composite shadows, the radial-gradient page background) do not map to DTCG primitives and are documented as strings.", "color": { "bg": { - "base": { "$type": "color", "$value": "#0b1118", "$description": "Page background (darkest layer). Also the de-facto theme color; use for a future PWA manifest theme_color." }, + "base": { "$type": "color", "$value": "#0b1118", "$description": "Page background (darkest layer). Also the de-facto theme color; used as the PWA manifest theme_color/background_color and the app-icon ground (manifest.webmanifest, ten31-app-icon.svg)." }, "panel": { "$type": "color", "$value": "#111a27", "$description": "Cards, sections, modals, sidebar, slide-over." }, "elevated": { "$type": "color", "$value": "#152233", "$description": "Elevated/hover panel state." }, "hover": { "$type": "color", "$value": "#1b2a3a", "$description": "Generic hover background." }, diff --git a/frontend/assets/apple-touch-icon.png b/frontend/assets/apple-touch-icon.png new file mode 100644 index 0000000..142a141 Binary files /dev/null and b/frontend/assets/apple-touch-icon.png differ diff --git a/frontend/assets/icon-192.png b/frontend/assets/icon-192.png new file mode 100644 index 0000000..d17f18f Binary files /dev/null and b/frontend/assets/icon-192.png differ diff --git a/frontend/assets/icon-512.png b/frontend/assets/icon-512.png new file mode 100644 index 0000000..6b6c234 Binary files /dev/null and b/frontend/assets/icon-512.png differ diff --git a/frontend/assets/ten31-app-icon.svg b/frontend/assets/ten31-app-icon.svg new file mode 100644 index 0000000..e022f77 --- /dev/null +++ b/frontend/assets/ten31-app-icon.svg @@ -0,0 +1,10 @@ + + + diff --git a/frontend/index.html b/frontend/index.html index 28cbfea..20f01e3 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -2,10 +2,23 @@ - + +