zotero-kb/docs/superpowers/specs/2026-04-15-import-card-two-step-design.md
Saberlve 5899e44791 docs: add two-step import+card generation design spec
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-15 16:56:59 +08:00

181 lines
6.6 KiB
Markdown
Raw Permalink 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.

# 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