docs: add two-step import+card generation design spec
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
b955d382e3
commit
5899e44791
180
docs/superpowers/specs/2026-04-15-import-card-two-step-design.md
Normal file
180
docs/superpowers/specs/2026-04-15-import-card-two-step-design.md
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
# 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
|
||||||
Loading…
Reference in New Issue
Block a user