Files
card-forge/.github/core-instructions.md
2025-07-17 16:15:23 +02:00

208 lines
9.0 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.

# Copilot Instruction Set **GoogleSheet  PhotoPDF** Wizard (SvelteKit)
> **Goal**
> A privacyfirst SvelteKit SPA that guides a user through importing a Google Sheet, mapping its columns, validating/culling rows, reviewing & cropping photos, and finally exporting two A4PDFs (text sheet + photo sheet). No personal data ever leaves the browser after the tab closes.
---
## 0. Tech Stack & Design Constraints
| Area | Decision |
| ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| Frontend | **SvelteKit** (latest) SPA mode (`prerender = false`) |
| State | **Svelte Stores** (writable & derived). No persistence beyond sessionStorage/IndexedDB. |
| Styling | **Tailwind CSS** + `@tailwind/typography`, base theme. Page background `bg-gray-50`. Use DaisyUI or shadcnui components if needed. |
| Auth & API | Google Identity Services (popup flow). Scopes: `spreadsheets`, `drive` (read/write). |
| Data storage | • Structured data → **IndexedDB** via `idb` helper. |
| • Image blobs → **Cache Storage** (`caches.open'photos-v1') a | |
| Face detection | **Mediapipe BlazeFace via TF.js (lite)** in a Web Worker. Confidence ≥ 0.8. |
| Virtual list | `svelte-virtual` (reactwindow equivalent). |
| PDF | **pdf-lib** (via ESM import). Embed custom font **Lato** (ttf provided in `/static/fonts`). |
---
## 1. User Stories & Acceptance Criteria
1. **US01 Splash** As a visitor I see a logo + “Start” button (no data loaded). Clicking starts the wizard.
2. **US02 SignIn** I authenticate with Google in a popup and grant the requested scopes. On success, token is cached in memory only.
3. **US03 Sheet Search** I type part of a Sheet name; a fuzzy list of my sheets appears (max 20). I select one.
4. **US04 Column Mapping** Columns AF preview (first 3 data rows). Under each header is a dropdown: Name / Surname / Nationality / Birthday / Picture URL / Ignore. All five fields must be mapped exactly once. Previous mapping is remembered for future sessions in `localStorage` (key: `columnHints`).
5. **US05 Row Validate & Select** I see rows in a virtualized table with validation states. Invalid rows (missing data, age < 18 or > 40) are preflagged. I can tick checkboxes to include/exclude. No inapp editing.
6. **US06 Photo Gallery** Lazy virtual grid of thumbnails. Auto facecrop overlay (dashed rectangle). Pictures with 0 or >1 faces are floated to top & flagged “Need manual crop”. I can drag/resize a fixedratio rectangle to override. Cropped area is stored as `{x,y,w,h}` in store.
7. **US07 Progress** While downloading or processing images / faces, I see a determinate progress bar (0100%). Each stage (download, face detect, PDF gen).
8. **US08 Generate PDFs** I click “Generate”. Two PDFs download:
* `people_data.pdf` textonly; grid fills as many boxes per A4 as fit (auto calculate cols/rows). Each box uses provided coordinates (see §4).
* `people_photos.pdf` same grid but with cropped headshot @ modest DPI, name beneath.
9. **US09 Cleanup** On window/tab unload, all IndexedDB records and CacheStorage entries are cleared.
---
## 2. Component Tree (Svelte)
```
+Layout.svelte (Tailwind container)
├─ Splash.svelte
├─ Wizard.svelte (stateful)
├─ StepAuth.svelte
├─ StepSheetSearch.svelte
├─ StepColumnMap.svelte
├─ StepRowFilter.svelte
├─ StepGallery.svelte
└─ StepGenerate.svelte
```
### Shared Stores (`src/lib/stores.ts`)
```ts
export const session = writable<{
token?: string;
user?: { name: string; email: string };
}>({});
export const sheetData = writable<RowData[]>(); // after mapping
export const pictures = writable<Record<RowID, PictureBlobInfo>>();
export const cropRects = writable<Record<RowID, Crop>>();
```
---
## 3. Data Flow
1. **Auth** ⇒ `session.token` set.
2. **Sheet fetch** (Google Sheets API) ⇒ raw 2D array.
3. **Mapping** ⇒ transform to `RowData` objects.
4. **Validation** ⇒ add `valid` flag; excluded rows removed from downstream.
5. **Download IMG** (Drive/HTTP) ⇒ cache → `pictures` store.
6. **Face Detect** (Worker) ⇒ proposed crop rectangle.
7. **Manual Crop** ⇒ update `cropRects`.
8. **Generate PDFs** ⇒ read `RowData`, `pictures`, `cropRects`.
---
## 4. PDF Layout Spec (mm → points helper)
* **Font**: Lato Regular, 10 pt. Embed once per doc via `pdfDoc.embedFont()`.
* **Grid**: auto compute `colWidth = 70 mm`, `rowHeight = 35 mm` for text; `imageBox = 30 mm × 30 mm` centred.
* **Coordinates (text sheet)** within each cell (0,0 is cell topleft):
* Full Name x 5 mm, y 5 mm
* Nationality x 5 mm, y 12 mm
* Birthday x 5 mm, y 19 mm
* **Coordinates (photo sheet)**
* Image topleft x 5 mm, y 5 mm (square)
* Name text centre below at y (5 + imageBox + 3 mm)
* Fit rows top→bottom, left→right. When page full, add new page.
---
## 5. Key Helpers
### `googleClient.ts`
* `initAuth(): Promise<Token>`
* `listSheets(query): Promise<SheetMeta[]>` (fuzzy)
* `getSheetValues(id, range): Promise<string[][]>`
### `faceWorker.ts` (Web Worker)
* Loads BlazeFace lite model once.
* `detectFaces(arrayBuffer) → {faces: Face[], blobURL}`
### `pdfGenerator.ts`
* `generateTextPDF(rows: RowData[]): Uint8Array`
* `generatePhotoPDF(rows: RowData[], crops: CropMap): Uint8Array`
---
## 6. Progress Bus (simple derived store)
```ts
export const progress = writable<{ stage: string; done: number; total: number }>();
```
Set from download loop and face detect worker.
---
## 7. Error Handling
* Auth popup closed ⇒ toast “Signin cancelled”.
* Sheet fetch 404 ⇒ return to StepSheetSearch with error banner.
* Image download error ⇒ mark row `pictureError = true`, bubble to top.
---
## 8. Folder Structure Skeleton
```
src/
lib/
stores.ts
googleClient.ts
faceWorker.ts
pdfGenerator.ts
routes/
+layout.svelte
index.svelte (Splash)
wizard/
+page.svelte (mount Wizard.svelte)
components/ (each Step*.svelte)
workers/
faceWorker.js
static/
fonts/Lato-Regular.ttf
```
---
## 9. Copilot Prompt Comments (templates)
### Example: `googleClient.ts`
```ts
/**
* Google API wrapper Browseronly, no refresh tokens.
* Prereqs: `<script src="https://accounts.google.com/gsi/client" async defer></script>` in root.
*
* Helper responsibilities:
* 1. Launch popup OAuth (scopes: spreadsheets, drive).
* 2. List up to 20 spreadsheets matching fuzzy query.
* 3. Download a specified range (A:Z) as 2D array.
* 4. Use inmemory token, autorefresh not required.
*/
```
### Example: `StepGallery.svelte`
```svelte
<script lang="ts">
/**
* Renders a virtual grid of downloaded photos.
* Highlights items needing manual crop (no face/multiple faces).
* When user drags the crop rectangle:
* Persist to cropRects store.
* Overlay rectangle turns green (valid).
*/
</script>
```
Copypaste these comment headers into the top of each file to prime GitHub Copilot with context.
---
## 10. Outstanding TODOs (owner = you)
* [ ] Provide exact mm → point conversion util if different spec required.
* [ ] Supply precise image/text box sizes & page margins.
* [ ] Drop in `Lato-Regular.ttf` into `/static/fonts`.
* [ ] Decide on logo / brand in Splash.
* [ ] Accessibility pass once UI stabilises.
---
### End of base instruction set
Feel free to ask for tweaks or deeper code snippets!