gendesign/.design-sync/NOTES.md

71 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# design-sync NOTES — tradein-mvp-frontend
Target: `tradein-mvp/frontend` (NOT the repo-root `frontend/`, which is the Site-Finder app). Project: gendesign-tradein.
## Build gotchas
- **No dist / no Storybook → synth-entry mode.** The package is a Next.js app, not a lib; `package.json` has no `main`/`module`/`exports`.
- **`--entry` must point at a NON-existent dist path** (`tradein-mvp/frontend/dist/index.js`). Two jobs:
1. PKG_DIR resolution walks up `dirname(--entry)` to the real named `package.json` → PKG_DIR=`tradein-mvp/frontend` (without `--entry`, PKG_DIR defaults to `node_modules/<pkg>` which doesn't self-install → ENOENT crash).
2. The path being absent makes `resolveDistEntry(soft)` return null → `synthEntry=true``deriveComponentsFromSrc` discovers components from `src/`. A path that EXISTS (e.g. package.json) suppresses synth mode → `[ZERO_MATCH]` tokens-only.
- **`cfg.*` path fields are PACKAGE-relative** (relative to PKG_DIR), not repo-relative. srcDir=`src`, cssEntry=`src/components/trade-in/trade-in.css`, tokensGlob=`src/app/globals.css`.
- `--node-modules tradein-mvp/frontend/node_modules` (react + @types/react live there).
## Styling
- Tokens in `src/app/globals.css` (`:root{--bg-app,--accent,…}`); component CSS in `src/components/trade-in/trade-in.css` (2277 lines). Manrope via remote Google Fonts `@import` (→ `[FONT_REMOTE]`, no action).
## Re-sync risks
- Synth scan over-includes non-component PascalCase exports (51 found vs ~37 files) — prune with `componentSrcMap: {Name: null}` as identified.
- Components are app-coupled (next/*, TanStack Query, data hooks); many need providers/mock props to render → expect floor cards / authored previews with composed props.
## Authoring (38 components, all authored-good)
- **Import contract:** preview imports `{X} from 'tradein-mvp-frontend'` (mapped to window.TradeInUI bundle) + data from `./_fixtures` (re-exports the repo's offline `app/ui-preview/estimate/fixture.ts`). Type imports erased.
- **Provider = seeded `PreviewProvider`** (`.design-sync/preview-provider.tsx`, wired via `cfg.extraEntries` + `cfg.provider`). Mirrors `app/ui-preview/estimate/page.tsx`'s seeded QueryClient: pre-fills `["auth","me"]` (fake admin user) + the estimate sub-queries (placement-history, house-analytics, sell-time-sensitivity, cian-price-changes=[], sales-vs-listings). This is what makes fetch-coupled cards (StreetDealsCard, HouseAnalyticsSection, PlacementHistoryCard) and auth-gated ones (RouteGuard, UserMenu) render offline.
- **CRITICAL:** an extraEntries module must NOT import anything that reads `process.env` at module top-level (e.g. `@/lib/useMe``@/lib/api`) — extraEntries evaluate BEFORE the synth-entry `.pkg-shim.mjs`, so `process` is undefined and the whole IIFE aborts (38/38 vanish from the global). The provider hardcodes `ME_QUERY_KEY = ["auth","me"]` instead of importing useMe.
- extraEntries path is PACKAGE-relative: `../../.design-sync/preview-provider.tsx`. Its `@/` aliases don't resolve from outside the tsconfig root → use relative `../tradein-mvp/frontend/src/...` for repo imports inside it.
- **Chart cards gate on analog count ≥8** (DistributionCard, ExposureCard): the shared fixture has 3 analogs → "not enough data" fallback. Those two previews extend `analogs` to 10 realistic ЕКБ lots INLINE in their own preview file (spread FIXTURE_ESTIMATE, override analogs+n_analogs) — never edit `_fixtures.ts`.
- **Admin/scraper panels** (DataQualitySection, ProviderProxySection, RunsTable, PacingSection, SystemHealthSection) fetch their own data with no repo fixture → they render their real styled LOADING/header state. Graded good as honest states; richer data would need admin fixtures that don't exist in the repo.
- **MapCard / MapPicker**: Leaflet from unpkg loads in the capture env; OSM raster tiles sometimes don't paint within the screenshot window (external tile fetch) — markers/popup/controls still render. Both graded good. MapPicker is a `createPortal` full-viewport overlay — rendered contained in capture; if a future capture viewport change makes it escape, add `cfg.overrides.MapPicker.cardMode`/`viewport`.
## Re-sync risks
- The `process` shim env defaults live in `.design-sync/overrides/source-kit.mjs` (synth entry). If the app reads NEW `process.env.NEXT_PUBLIC_*` vars, add them there or components throw.
- `PreviewProvider` seed keys are pinned to the fixture's `estimate_id` and address/area/rooms — if `fixture.ts` changes those, update `preview-provider.tsx` to match (else fetch-coupled cards re-floor).
- Synth scan re-includes Next route/layout/error files on every build → `componentSrcMap` nulls (13 entries) must persist. New app-router pages would need adding.
- `.d.ts` props are weak (`[key:string]:unknown`) — synth mode has no built types. Real prop contracts live in each component's source `interface Props`. A real `tsup`/`tsc` lib build would fix this (recommend if the agent needs strong API contracts).
- Grades clear on any `cfg.provider`/preview-affecting config change (expected) — re-grade from fresh sheets.
## Re-sync 2026-07-02 — v2 dashboard sync (main was 223 commits ahead; harness authored on June components)
**Two source-kit.mjs fork fixes were REQUIRED for the evolved v2 codebase (both committed in the override, declared in cfg.libOverrides):**
1. **Exclude `next/font` importers from the synth-entry.** `src/app/v2/layout.tsx` calls `Manrope()`/`IBM_Plex_Mono()` from `next/font/google` at MODULE TOP-LEVEL. esbuild can't resolve the Next-only loader → stubs it `(void 0)``undefined()` aborts the whole browser IIFE → `window.TradeInUI` empty → ALL 58 components vanish (`[RENDER] root empty` everywhere, `[BUNDLE_EXPORT]`). Fix: `comps` filter drops any file whose content matches `/from\s+['"]next\/font/`. If a NEW file top-level-calls a Next build-time loader, same class of crash — extend the filter.
2. **Re-export default-exported components.** `export * from <path>` does NOT re-export a module's `default`. The v2 views/nav/overlay/panel are authored `export default function <Name>` (AnalyticsView, HeroBar, HistoryView, ParamsPanel, SectionOverlay, SourcesView, TopNav) → absent from `window.TradeInUI``[BUNDLE_EXPORT] not a component`. Fix: entry now also emits `export { default as <Name> } from <path>` for each `export default function/class <Name>`. Named-export components (`export function X`) were always fine.
**componentSrcMap:** added `TradeInV2Layout / TradeInV2Page / SaleShareLayout / SaleSharePage` = null (Next route files, never DS components — same as RootLayout et al.).
**overrides (grid):** MapPicker `{cardMode:single, primaryStory:Default}` (portal/fixed), StreetDealsCard + Topbar `{cardMode:column}` (wider than a grid cell).
**Floor-card components (6, authorable on any future re-sync):** SaleShareControls, SaleShareList, SaleShareMap, SectionOverlay, LocationDrawer, BuildingListingsDrawer — overlays/drawers whose props don't seed rich data via PreviewProvider, so they show the honest typographic floor. All the OTHER v2 components (views/nav/hero/panel) render richly because the provider seeds their data.
**Known render warn:** ResultPanel — `variants identical` (Default vs Error cells render near-identically; not broken, the Error cell just doesn't diverge visually enough). Recorded here so re-syncs don't read it as new.
**Font note (re-sync risk):** the v2 HUD's real typefaces are **Manrope + IBM_Plex_Mono** (loaded by the excluded `v2/layout.tsx` via next/font → CSS vars `--font-manrope`/`--font-plex-mono`). `cfg.extraFonts` ships **Inter + JetBrains Mono** (June brand fonts). v2 previews therefore render Manrope-slot text in the shipped fallback. If brand-exact v2 rendering is wanted, add Manrope + IBM Plex Mono woff2 to `.design-sync/fonts/` + `cfg.extraFonts` and map the `--font-manrope`/`--font-plex-mono` vars.
**ResultPanel NAME COLLISION (fixed 2026-07-02):** two components named `ResultPanel` in src — `app/scrapers/_components/ScraperPage.tsx` (`export function ResultPanel`, scraper mut-panel) and `components/trade-in/v2/ResultPanel.tsx` (`export default function ResultPanel`, the v2 estimate result / honest hero). The default-re-export fix makes the v2 one win `window.TradeInUI.ResultPanel` (explicit `export {default as ResultPanel}` beats the star export). But the June authored preview `previews/ResultPanel.tsx` was the SCRAPER one (`mut` props) → the v2 component rendered fine (reads data from context) but its prompt.md documented the wrong (scraper) API. FIX: (1) `cfg.componentSrcMap.ResultPanel = "src/components/trade-in/v2/ResultPanel.tsx"` pins enrichment to v2; (2) re-authored `previews/ResultPanel.tsx``<ResultPanel onNavigate={()=>{}} />` (v2 API; `data` defaults to the component's built-in RESULT_FIXTURE = honest-hero). If a re-sync ever shows ResultPanel with scraper markup, the pin/preview regressed. Any NEW duplicate PascalCase component name will hit the same class of issue — the default-re-export makes the default-export win; pin + author the intended one.
**Upload gotcha (this machine):** after a follow-up single-component rebuild, `resync-verdict.json upload.deletePaths` came back with ALL other components (318 paths) — a stale-anchor diff artifact, NOT real deletions (ResultPanel + audit/uploads were absent from it). Do NOT feed that deletePaths to delete_files (would nuke the project). For a focused single-component re-upload: write just that component's `components/<group>/<Name>/*` + `_preview/<Name>.js` + `_ds_sync.json` (sentinel-fenced), deletes=[].
- **CORRECTION (2026-07-03):** the 318 deletePaths were NOT a diff artifact — they were REAL local deletions caused by a source-kit fork bug (see below): the July-02 `componentSrcMap.ResultPanel` pin collapsed the synth build to 1 component, so the local ds-bundle genuinely lost the other 53. The "don't feed deletePaths blindly" advice stands (it saved the project), but the root cause is fixed now.
## Re-sync 2026-07-03 — 6 floor cards authored + v2 brand fonts (#2267)
**source-kit.mjs fork fix #3 (REQUIRED): componentSrcMap pin must AUGMENT synth discovery, not replace it.** In synth mode there is no shipped `.d.ts`, so `exportedNames()` is empty and `names` contains ONLY the non-null `componentSrcMap` pins. The old guard `if (!components.length && synthEntry) components = deriveComponentsFromSrc(...)` therefore never ran once a single pin existed (ResultPanel, added 2026-07-02) → the whole bundle collapsed to 1 component (`(stale preview: X — component no longer exported)` for everything else; verdict shows all others as `removed` + bogus deletePaths). Fix in the fork: when `synthEntry`, always union `deriveComponentsFromSrc(srcFiles)` (minus `null`-excluded) with the pinned names. Any future pin would have re-triggered this. NB: the fork edit re-keys EVERY component's sourceKey → full re-grade pass (done: 47/54 renderHashes byte-identical to the 2026-07-02 anchor → carry-forward grades; the rest eyeballed).
**6 floor cards → authored (all graded good):**
- `SaleShareControls` — inline `SaleShareSummary` (histogram 7 корзин, coverage 92.1%); порог 8% приглушает нижнюю корзину; все фильтры.
- `SaleShareList` — 6 инлайн-домов ЕКБ (heat-бейджи, «аварийный», over_100 «возможно, несколько корпусов», selected-row) + Empty cell. `cardMode: column`.
- `SaleShareMap` — 8 heat-маркеров + открытый popup выбранного дома; OSM-тайлы офлайн не красятся (известно, как MapCard).
- `SectionOverlay` (v2) — рендерится contained в relative-«артборде» (absolute-позиционирование против ближайшего positioned ancestor; wrapper height 680 + v2-градиент). 2 cells: HistoryView (04) / AnalyticsView (06) на их встроенных fixtures (data-props не переданы). `cardMode: column`.
- `LocationDrawer` (v2) — open=true в таком же contained-артборде (height 640). `cardMode: column`.
- `BuildingListingsDrawer``createPortal` + `.ss-drawer-overlay` = position:fixed → `cardMode: single` (прецедент MapPicker). Шапка богатая (props), тело fetch-coupled (`useBuildingListings`, data-prop нет) → офлайн честный error-state «Не удалось загрузить объявления дома». Сидировать можно было бы ключом `["sale-share","listings",<house_id>]` в PreviewProvider — сознательно НЕ сделано (кэш-ключ завязан на house_id фикстуры превью; хрупко).
**v2 brand fonts shipped (Manrope + IBM Plex Mono).** woff2 (latin+cyrillic; Manrope variable 200-800, Plex Mono static 300/400/500 — веса из `app/v2/layout.tsx`) скачаны с Google Fonts → `.design-sync/fonts/`, @font-face добавлены в `brand-fonts.css`. **ГОЧА: extraFonts-пайплайн (`css.mjs extractFonts`) извлекает ТОЛЬКО `@font-face`-блоки — `:root{}` из brand-fonts.css молча выбрасывается.** Маппинг `--font-manrope`/`--font-plex-mono` поэтому живёт в `preview-provider.tsx` (`<style>` в провайдере — капчер-путь) + задокументирован для design-консюмеров в `conventions.md` (сниппет `:root{...}`). После фикса v2-цифры реально в Plex Mono (проверено по sheets ResultPanel/SectionOverlay).
**Upload 2026-07-03: НЕ выполнен из worker-сессии** — DesignSync MCP-тулов в ней нет (ToolSearch пуст). Verdict готов и чист: ok=true, pendingGrade=0, deletePaths=0 (легитимно — remote-анкор полный, local снова 54 компонента), upload.any=true, components=54 (все re-key'нуты форк-фиксом), bundle+styling+aux=true. Main-сессия: залить ds-bundle по verdict'у (deletePaths пуст — ничего не удалять) и после успеха скопировать свежий `ds-bundle/_ds_sync.json``.design-sync/.cache/remote-sync.json` (новый анкор).