commit 0c0c773e61666f17a7c4cc4f5eabdfe3b7ae7a6d Author: Saberlve Date: Wed Apr 15 11:48:43 2026 +0800 docs: add zotero collection import design diff --git a/docs/superpowers/specs/2026-04-15-zotero-collection-import-design.md b/docs/superpowers/specs/2026-04-15-zotero-collection-import-design.md new file mode 100644 index 0000000..f9f3292 --- /dev/null +++ b/docs/superpowers/specs/2026-04-15-zotero-collection-import-design.md @@ -0,0 +1,336 @@ +# Zotero Collection Import Design + +## Overview + +This design upgrades the current left-panel import area from two temporary flows: + +- import current selection from a bridge snapshot +- text-search fallback import + +into one unified Zotero collection importer. + +The new importer must visualize the user's current Zotero collection hierarchy, let the user browse collections, display the literature under the selected collection, remember cross-collection checkbox state, and batch-import selected items into the current project. + +This work is intentionally scoped to the import surface only. The project card panel and writing panel remain in place. + +## Goals + +- Replace the current left-panel import widgets with one unified `Import From Zotero` section +- Visualize Zotero collections as a tree in the left panel +- When a collection is selected, show the literature under that collection and all descendant collections +- Support checkbox-based multi-selection across collection switches +- Remember previous selection state while the page remains open +- Support bulk actions: + - select all items from the current collection plus descendants + - clear all selected items + - import selected items into the current project +- Preserve the existing project panel, card panel, and writing panel structure + +## Non-Goals + +- Full drag-and-drop tree editing +- Editing Zotero collections +- Multi-pane import wizard +- Persisting import selections across browser restarts or across different users +- Replacing the bridge-based current-selection import flow at the API level + +The bridge-based import endpoint may remain available, but it is no longer the main UI path. + +## Product Scope + +The importer is a left-panel replacement only. + +### Unchanged Areas + +- project creation +- project list +- project card list +- card detail viewer +- writing recommendation form +- writing plan form + +### Replaced Area + +The current pair of sections: + +- `导入当前选中` +- `后备导入` + +will be removed and replaced with: + +- `从 Zotero 导入` + +## User Flow + +1. The user selects a project +2. The user browses the Zotero collection tree +3. The user clicks one collection +4. The UI loads all literature from that collection and all descendants +5. The user checks items to import +6. The user may switch to a different collection and continue selecting +7. Previously selected items remain selected +8. The user clicks `导入所选到当前项目` +9. The backend imports all selected items into the current project +10. The card panel refreshes with the newly imported cards + +## Layout + +The page stays three-column. + +### Left Panel + +The left panel contains: + +1. `创建项目` +2. `项目列表` +3. `从 Zotero 导入` + +### Import Section Structure + +The import section has three internal regions: + +1. collection tree +2. collection item list +3. sticky action bar + +#### Collection Tree + +- tree view with expand/collapse state +- current collection highlighted +- each node shows: + - collection name + - descendant-aware item count + +#### Collection Item List + +- shows items from the current collection and all descendant collections +- each row contains: + - checkbox + - title + - year + - item type + - compact metadata +- rows already selected in other collections remain checked when shown again + +#### Sticky Action Bar + +- selected count +- `全选当前目录及子目录` +- `清空选择` +- `导入所选到当前项目` + +The action bar remains visible while scrolling within the import section. + +## Interaction Semantics + +### Collection Selection + +Clicking a collection: + +- sets it as the active collection +- loads all items under that collection plus descendants +- does not clear already selected items + +### Checkbox State + +Selection is tracked by `item_key`, not by visible row index. + +This means: + +- switching collections does not lose selection +- re-entering a collection restores prior checkbox state +- the selected count reflects the union of all selected items across the session + +### Select-All Behavior + +`全选当前目录及子目录` means: + +- take the currently displayed item set +- merge all of those item keys into the global selected set +- keep existing selections from other collections intact + +### Clear Behavior + +`清空选择` means: + +- clear the full selected item set +- uncheck all visible rows +- reset the selected count to zero + +### Import Behavior + +`导入所选到当前项目` means: + +- require a currently selected project +- submit the full selected item key set to the backend +- keep the active collection and tree expansion state +- clear the selected item set on success +- refresh the project card list + +## Frontend State Model + +The page should maintain these state keys: + +- `selectedCollectionKey` +- `expandedCollectionKeys` +- `selectedItemKeys` +- `visibleCollectionItems` + +### State Rules + +- `selectedCollectionKey` changes when the user clicks a tree node +- `expandedCollectionKeys` changes when nodes are opened or closed +- `selectedItemKeys` persists across collection switches during the current browser session +- `visibleCollectionItems` updates whenever the active collection changes + +`selectedItemKeys` is the source of truth for checkbox rendering and batch import. + +## Backend API Additions + +Three API capabilities are required. + +### 1. Collection Tree + +`GET /api/zotero/collections/tree` + +Returns the current Zotero collection hierarchy. + +Each node must include: + +- `collection_key` +- `name` +- `parent_key` +- `children` +- `direct_item_count` +- `descendant_item_count` + +### 2. Collection Items + +`GET /api/zotero/collections/{collection_key}/items?include_descendants=true` + +Returns all regular Zotero items under the selected collection. In V1 of this feature, the UI will always request `include_descendants=true`. + +Each returned item must include: + +- `item_key` +- `title` +- `year` +- `item_type` +- `abstract` +- `collection_paths` + +### 3. Batch Import + +`POST /api/projects/{project_id}/imports/item-keys` + +This endpoint already exists and should remain the import execution path for the new UI. + +Input: + +```json +{ + "item_keys": ["ABCD1234", "EFGH5678"] +} +``` + +Output: + +- `project_id` +- `imported_item_keys` +- `project_view` + +## Reader Responsibilities + +The Zotero reader must support collection-tree import without depending on bridge snapshots. + +Required reader capabilities: + +- build normalized collection tree data +- compute direct item counts per collection +- compute descendant-aware item counts per collection +- list all regular items under a collection with optional descendant inclusion + +The reader should continue to ignore attachment, note, and annotation rows as import roots. + +## Error Handling + +### No Project Selected + +- disable import submission or return a clear UI error +- do not clear selected checkboxes + +### Collection Tree Load Failure + +- show error state in the import section +- do not affect cards or writing sections + +### Empty Collection + +- show an empty-state list +- keep tree navigation usable + +### Partial Import Failure + +- report imported item keys and failed item keys separately if the backend can distinguish them +- do not silently report full success if part of the request failed + +### Missing Local Zotero Data + +- show import section unavailable +- preserve the rest of the application + +## Testing Strategy + +### Reader Tests + +- build collection tree from fixture Zotero data +- compute descendant-aware item counts +- list collection items with descendant inclusion + +### API Tests + +- `GET /api/zotero/collections/tree` +- `GET /api/zotero/collections/{collection_key}/items?include_descendants=true` +- `POST /api/projects/{project_id}/imports/item-keys` + +### UI Tests + +The index template must include: + +- collection tree container +- collection item list container +- selected count area +- select-all button +- clear-selection button +- import-selected button + +## Constraints And Tradeoffs + +### Why Tree + Side List Instead Of Tree-Mixed Items + +The user explicitly chose: + +- collection tree on one side +- item list shown after selecting a collection + +This keeps scanning manageable for large libraries and avoids turning the tree itself into an overloaded mixed node structure. + +### Why Session-Scoped Selection Memory + +The user explicitly asked to remember previous selection state, but not to store durable drafts of import batches. Session-scoped state is enough for the workflow and avoids backend persistence complexity. + +### Why Keep Cards And Writing Panels Unchanged + +The import feature is a left-panel responsibility. Expanding it into a page-level mode would create unnecessary layout churn and broaden the implementation risk. + +## Acceptance Criteria + +This feature is complete when: + +- the old `导入当前选中` and `后备导入` UI sections are gone +- the left panel shows a collection tree and descendant-aware collection item list +- selecting items survives collection switches during the session +- the user can select all items from a collection plus descendants +- the user can clear all current selections +- the user can batch-import the selected items into the active project +- the project card area refreshes after a successful import