9.0 KiB
Copilot Instruction Set – Google‑Sheet → Photo‑PDF Wizard (SvelteKit)
Goal A privacy‑first SvelteKit SPA that guides a user through importing a Google Sheet, mapping its columns, validating/culling rows, reviewing & cropping photos, and finally exporting two A4‑PDFs (text sheet + photo sheet). No personal data ever leaves the browser after the tab closes.
0. Tech Stack & Design Constraints
| Area | Decision |
|---|---|
| Front‑end | 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 shadcn‑ui 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 (react‑window equivalent). |
pdf-lib (via ESM import). Embed custom font Lato (ttf provided in /static/fonts). |
1. User Stories & Acceptance Criteria
-
US‑01 Splash – As a visitor I see a logo + “Start” button (no data loaded). Clicking starts the wizard.
-
US‑02 Sign‑In – I authenticate with Google in a popup and grant the requested scopes. On success, token is cached in memory only.
-
US‑03 Sheet Search – I type part of a Sheet name; a fuzzy list of my sheets appears (max 20). I select one.
-
US‑04 Column Mapping – Columns A–F 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). -
US‑05 Row Validate & Select – I see rows in a virtualized table with validation states. Invalid rows (missing data, age < 18 or > 40) are pre‑flagged. I can tick checkboxes to include/exclude. No in‑app editing.
-
US‑06 Photo Gallery – Lazy virtual grid of thumbnails. Auto face‑crop overlay (dashed rectangle). Pictures with 0 or >1 faces are floated to top & flagged “Need manual crop”. I can drag/resize a fixed‑ratio rectangle to override. Cropped area is stored as
{x,y,w,h}in store. -
US‑07 Progress – While downloading or processing images / faces, I see a determinate progress bar (0‑100%). Each stage (download, face detect, PDF gen).
-
US‑08 Generate PDFs – I click “Generate”. Two PDFs download:
people_data.pdf– text‑only; 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.
-
US‑09 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)
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
- Auth ⇒
session.tokenset. - Sheet fetch (Google Sheets API) ⇒ raw 2D array.
- Mapping ⇒ transform to
RowDataobjects. - Validation ⇒ add
validflag; excluded rows removed from downstream. - Download IMG (Drive/HTTP) ⇒ cache →
picturesstore. - Face Detect (Worker) ⇒ proposed crop rectangle.
- Manual Crop ⇒ update
cropRects. - 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 mmfor text;imageBox = 30 mm × 30 mmcentred. -
Coordinates (text sheet) – within each cell (0,0 is cell top‑left):
- 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 top‑left 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[]): Uint8ArraygeneratePhotoPDF(rows: RowData[], crops: CropMap): Uint8Array
6. Progress Bus (simple derived store)
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 “Sign‑in 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
/**
* Google API wrapper – Browser‑only, 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 2‑D array.
* 4. Use in‑memory token, auto‑refresh not required.
*/
Example: StepGallery.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>
Copy‑paste 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.ttfinto/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!