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

6.6 KiB
Raw Permalink Blame History

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_keys associated with the project
  • projects/{project_id}/pending-items.jsonremoved — 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:

{
  "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:

{
  "item_keys": ["EFGH5678", "IJKL9012"]
}

Response (streaming or incremental):

{
  "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