# Import + Card Generation Two-Step Design ## Status Draft — not yet approved. ## Overview Split the current "import文献 + immediately generate cards" flow into two distinct steps: 1. **Import** — items are registered to a project, but cards are NOT generated yet. The system checks a global registry for existing cards. 2. **Manual batch generate** — user manually selects pending items and triggers card generation. Same item across different projects reuses existing cards (hash-based deduplication). ## Data Model ### Global Registry (existing, unchanged) - `library/index/items.json` — global item metadata registry, keyed by `item_key` - `library/index/cards.json` — global card index, keyed by `item_key` ### Project Registry (existing, extended) - `projects/{project_id}/selected-items.json` — list of `item_key`s associated with the project - `projects/{project_id}/pending-items.json` — **removed** — pending status is computed dynamically as `selected_items` minus keys present in `cards.json`. No separate tracking file needed. ### Import State For each item in a project, one of two states: | State | Meaning | |-------|---------| | `pending` | item is imported but no card exists in `cards.json` | | `done` | card exists in `cards.json` for this item_key | ## API Endpoints ### `GET /api/projects/{project_id}/import-state` Returns the import state of all items in a project. **Response:** ```json { "project_id": "proj-123", "items": [ { "item_key": "ABCD1234", "title": "Paper Title", "creators": ["Author A", "Author B"], "year": 2024, "item_type": "journalArticle", "card_status": "done" }, { "item_key": "EFGH5678", "title": "Another Paper", "creators": ["Author C"], "year": 2023, "item_type": "conferencePaper", "card_status": "pending" } ], "pending_count": 1, "done_count": 1 } ``` **Logic:** Iterate `selected-items.json`, for each `item_key` check if it exists in `cards.json`. If yes → `done`, else → `pending`. ### `POST /api/projects/{project_id}/cards/generate` Receives a list of item_keys to generate cards for. Generates cards one by one, updates `cards.json`. **Request:** ```json { "item_keys": ["EFGH5678", "IJKL9012"] } ``` **Response (streaming or incremental):** ```json { "project_id": "proj-123", "generated": ["EFGH5678"], "failed": [], "items": [ { "item_key": "EFGH5678", "title": "...", "creators": [...], "card_status": "done" } ] } ``` **Logic:** 1. For each `item_key` in `item_keys`: - Read item metadata from `items.json` (or from Zotero reader if not yet in items.json) - Call `CardBuilder.build_or_update()` which computes `source_hash` and either creates or updates the card - Send incremental progress event (SSE or via callback) 2. After all complete, return the updated item states. ### `POST /api/projects/{project_id}/imports/item-keys` (existing, extended) When items are imported, they are added to `selected-items.json`. Cards are NOT generated immediately. ## Frontend: Import Window Layout ### Two-Column Layout ``` ┌─────────────────────────────────────────────────────────┐ │ [title bar: 导入文献] [_] [□] [×] │ ├───────────────────────┬─────────────────────────────────┤ │ LEFT (40%) │ RIGHT (60%) │ │ ┌─────────────────┐ │ ┌───────────────────────────┐ │ │ │ Search... │ │ │ Title: ... │ │ │ └─────────────────┘ │ │ Authors: ... │ │ │ ┌─────────────────┐ │ │ Year: ... │ │ │ │ □ Paper A [✓] │ │ │ Type: journalArticle │ │ │ │ ☑ Paper B │ │ │ Abstract: ... │ │ │ │ ✓ Paper C │ │ │ Tags: tag1, tag2 │ │ │ │ □ Paper D │ │ └───────────────────────────┘ │ │ └─────────────────┘ │ │ │ Pending (2) Done (1) │ │ ├───────────────────────┴─────────────────────────────────┤ │ [生成选中卡片 (2)] │ └─────────────────────────────────────────────────────────┘ ``` ### Visual States - **pending items** — light background, checkbox visible, unchecked by default - **done items** — green tint / checkmark, no checkbox, not selectable for generation - **selected pending** — highlighted border, checkbox checked - **generating** — spinner icon, row slightly dimmed ### Interactions 1. Click item in left list → right panel shows metadata preview 2. Click checkbox on pending item → toggles selection 3. "Select All Pending" link at top of list 4. Click "生成选中卡片 (N)" button → triggers `POST .../cards/generate` 5. During generation: button becomes progress bar "生成 X/Y",completed items turn done in real time ### Card Status Legend Shown at bottom of left panel: ``` Pending (N) ● Done (N) ✓ ``` ## Deduplication Logic `CardBuilder.build_or_update()` computes `source_hash` from: ``` { item_key, title, creators, year, item_type, abstract, tags, collection_paths, notes, attachment_texts } ``` - If `cards.json` already has an entry for this `item_key` with the same `source_hash` → skip LLM call, card already up to date - If hash differs → regenerate (overwrite) - This means the same `item_key` imported into multiple projects will reuse the same card if the source hasn't changed ## Error Handling - If a single item fails to generate (LLM error, missing data), mark it as failed and continue with others - Failed items shown with error icon, can be retried individually or as batch - Return `failed` array in response with error reasons ## Out of Scope - Streaming/SSE for real-time progress (use simple polling or single request for now) - Background job queue - Version history for regenerated cards