432 lines
13 KiB
Markdown
432 lines
13 KiB
Markdown
# Zotero Collection Import Design
|
|
|
|
## Overview
|
|
|
|
This design revises the current import flow into a true full-screen modal experience:
|
|
|
|
- the main page keeps project, cards, and writing views visible until import starts
|
|
- clicking `导入文献` opens a full-screen modal layer that visually occupies the whole viewport
|
|
|
|
The import modal visualizes the current Zotero collection hierarchy, lets the user browse collections, displays only the directly contained literature for the selected collection, remembers cross-collection checkbox state for the current browser session, and batch-imports the 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 import widgets with one `导入文献` button that opens a full-screen modal
|
|
- Visualize Zotero collections as a tree on the left side of the full-screen modal
|
|
- When a collection is selected, show only the items directly contained by that collection
|
|
- Support checkbox-based multi-selection across collection switches
|
|
- Remember previous selection state while the page remains open, even if the modal is closed and reopened
|
|
- Support top-bar actions:
|
|
- clear all selected items
|
|
- import selected items into the current project
|
|
- Keep the item list compact by default:
|
|
- show checkbox, title, and year only
|
|
- show abstract only after hover delay or explicit details trigger
|
|
- Preserve the existing project panel, card panel, and writing panel structure outside the modal
|
|
|
|
## Non-Goals
|
|
|
|
- Full drag-and-drop tree editing
|
|
- Editing Zotero collections
|
|
- Multi-step import wizard
|
|
- Persisting import selections across browser restarts or across different users
|
|
- Replacing the bridge-based current-selection import flow at the API level
|
|
- Showing abstract text inline for every row by default
|
|
- Collection-level bulk selection such as “select all current collection and descendants”
|
|
|
|
The bridge-based import endpoint may remain available, but it is no longer the main UI path.
|
|
|
|
## Product Scope
|
|
|
|
The importer is a modal-based subflow launched from the main page.
|
|
|
|
### 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:
|
|
|
|
- one `导入文献` button on the main page
|
|
- one full-screen `Import From Zotero` modal
|
|
|
|
## User Flow
|
|
|
|
1. The user selects a project or leaves the page without selecting one yet
|
|
2. The user clicks `导入文献`
|
|
3. A full-screen modal opens
|
|
4. The user browses the Zotero collection tree on the left
|
|
5. The user clicks one collection
|
|
6. The UI loads only the items directly contained by that collection
|
|
7. The user checks individual items to import
|
|
8. The user may switch to a different collection and continue selecting
|
|
9. Previously selected items remain selected
|
|
10. The user clicks `导入所选到当前项目`
|
|
11. The backend imports all selected items into the current project
|
|
12. The card panel refreshes with the newly imported cards
|
|
13. The modal closes on success
|
|
|
|
## Layout
|
|
|
|
The page stays three-column until the import modal opens.
|
|
|
|
### Main Page
|
|
|
|
The left panel contains:
|
|
|
|
1. `创建项目`
|
|
2. `项目列表`
|
|
3. `导入文献`
|
|
|
|
The collection tree and collection item list are not rendered inline on the main page.
|
|
|
|
### Full-Screen Modal
|
|
|
|
The modal fills the viewport and has three internal regions:
|
|
|
|
1. top toolbar
|
|
2. modal body
|
|
3. full-screen overlay/background layer
|
|
|
|
#### Top Toolbar
|
|
|
|
- `返回主页面`
|
|
- current project name or explicit `未选择项目`
|
|
- selected count
|
|
- `清空选择`
|
|
- `导入所选`
|
|
|
|
This toolbar remains visible while the collection tree and item list scroll.
|
|
|
|
#### Modal Body
|
|
|
|
- desktop: fixed two-column layout
|
|
- left: collection tree
|
|
- right: collection item list
|
|
- narrow screens: may stack or collapse the tree, but the desktop target is explicitly left-tree / right-list
|
|
|
|
#### Collection Tree
|
|
|
|
- tree view with expand/collapse state
|
|
- current collection highlighted
|
|
- each node shows:
|
|
- collection name
|
|
- item count for that collection
|
|
|
|
#### Collection Item List
|
|
|
|
- shows only items directly contained in the selected collection
|
|
- each row contains:
|
|
- checkbox
|
|
- title
|
|
- year
|
|
- abstract is not shown inline
|
|
- rows already selected in other collections remain checked when shown again
|
|
|
|
## Interaction Semantics
|
|
|
|
### Modal Open And Close
|
|
|
|
Clicking `导入文献` opens the full-screen modal.
|
|
|
|
The modal can be closed by:
|
|
|
|
- clicking the explicit return button
|
|
- clicking the overlay if that behavior is preserved
|
|
- pressing `Esc`
|
|
|
|
Closing the modal does not clear:
|
|
|
|
- `selectedCollectionKey`
|
|
- `expandedCollectionKeys`
|
|
- `selectedItemKeys`
|
|
|
|
Reopening the modal restores the previous browsing and selection state for the current page session.
|
|
|
|
### Collection Selection
|
|
|
|
Clicking a collection:
|
|
|
|
- sets it as the active collection
|
|
- loads only items directly contained by that collection
|
|
- 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
|
|
|
|
### 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
|
|
- remain disabled when no project is selected
|
|
- 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
|
|
- close the modal on success
|
|
- refresh the project card list
|
|
|
|
## Abstract Preview Behavior
|
|
|
|
The item list stays compact by default.
|
|
|
|
### Desktop
|
|
|
|
- hovering the title area of an item row starts a 3 second timer
|
|
- if the pointer is still on the row after 3 seconds, an abstract preview popover appears
|
|
- if the abstract is empty, no popover appears
|
|
- moving the pointer away before 3 seconds cancels the timer
|
|
- moving the pointer away after the popover appears closes it
|
|
- only one preview popover may be open at a time
|
|
|
|
### Mobile And Narrow-Screen Fallback
|
|
|
|
Hover is not reliable on touch devices, so the UI must degrade to an explicit preview trigger such as a small details button per row.
|
|
|
|
This fallback:
|
|
|
|
- opens the same abstract preview content
|
|
- does not rely on hover timing
|
|
- does not change batch-selection behavior
|
|
|
|
### Preview Cancellation Rules
|
|
|
|
The preview timer or popover closes immediately when:
|
|
|
|
- the user leaves the row
|
|
- the user scrolls the item list
|
|
- the user changes collection
|
|
- the user clicks a checkbox
|
|
|
|
## Frontend State Model
|
|
|
|
The page should maintain these state keys:
|
|
|
|
- `isImportModalOpen`
|
|
- `selectedCollectionKey`
|
|
- `expandedCollectionKeys`
|
|
- `selectedItemKeys`
|
|
- `visibleCollectionItems`
|
|
- `selectedItemMeta`
|
|
- `hoveredItemKey`
|
|
- `hoverTimerId`
|
|
- `previewItemKey`
|
|
|
|
### State Rules
|
|
|
|
- `isImportModalOpen` changes when the user opens or closes the modal
|
|
- `selectedCollectionKey` changes when the user clicks a tree node
|
|
- `expandedCollectionKeys` changes when nodes are opened or closed
|
|
- `selectedItemKeys` persists across collection switches and modal reopen during the current browser session
|
|
- `visibleCollectionItems` updates whenever the active collection changes
|
|
- `selectedItemMeta` stores minimal metadata for already selected items
|
|
- `hoveredItemKey`, `hoverTimerId`, and `previewItemKey` control delayed abstract preview
|
|
|
|
`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=false`
|
|
|
|
Returns only regular Zotero items directly contained by the selected collection.
|
|
|
|
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
|
|
- 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
|
|
|
|
- allow the modal to open and load Zotero data
|
|
- disable import submission with a clear UI message
|
|
- do not clear selected checkboxes
|
|
|
|
### Collection Tree Load Failure
|
|
|
|
- show error state inside the modal
|
|
- 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
|
|
|
|
- disable or error the modal content clearly
|
|
- preserve the rest of the application
|
|
|
|
## Testing Strategy
|
|
|
|
### Reader Tests
|
|
|
|
- build collection tree from fixture Zotero data
|
|
- compute direct item counts
|
|
- list collection items without descendant inclusion
|
|
|
|
### API Tests
|
|
|
|
- `GET /api/zotero/collections/tree`
|
|
- `GET /api/zotero/collections/{collection_key}/items?include_descendants=false`
|
|
- `POST /api/projects/{project_id}/imports/item-keys`
|
|
|
|
### UI Tests
|
|
|
|
The main page must include:
|
|
|
|
- import-modal trigger button
|
|
|
|
The modal must include:
|
|
|
|
- collection tree container
|
|
- collection item list container
|
|
- selected count area
|
|
- clear-selection button
|
|
- import-selected button
|
|
|
|
UI behavior must cover:
|
|
|
|
- full-screen modal opens from the trigger button
|
|
- import action is disabled when no project is selected
|
|
- closing and reopening the modal preserves selected items during the session
|
|
- import success closes the modal and refreshes cards
|
|
- abstract preview appears only after the hover delay on desktop
|
|
- item rows display title and year by default, not abstract text
|
|
|
|
## Constraints And Tradeoffs
|
|
|
|
### Why Full-Screen Modal Instead Of A Centered Dialog
|
|
|
|
The user explicitly wants the import experience to feel like a whole-page task, not a cramped dialog anchored inside the left side of the UI. A full-screen modal preserves modal semantics while matching that expectation visually.
|
|
|
|
### Why Collection Tree + Direct Item List
|
|
|
|
The user explicitly chose:
|
|
|
|
- collection tree on the left
|
|
- item list on the right
|
|
- only direct items for the selected collection
|
|
|
|
This keeps the browsing model simple: collection chooses the scope, list chooses the individual papers.
|
|
|
|
### Why No Collection-Level Bulk Select
|
|
|
|
The user explicitly wants to choose papers one by one, not by collection-level bulk action. The collection tree filters the list; it does not act as a selection shortcut.
|
|
|
|
### Why Title + Year By Default
|
|
|
|
The user explicitly wanted a lighter list than the current dense layout. Showing title and year by default preserves scanning context while leaving abstracts to delayed preview.
|
|
|
|
### 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 subflow. Expanding it into a permanent page layout 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 main page shows a `导入文献` button instead of an inline importer
|
|
- clicking the button opens a full-screen modal with collection tree and direct-item list
|
|
- selecting items survives collection switches and modal reopen during the session
|
|
- the user can clear all current selections
|
|
- the user can batch-import the selected items into the active project
|
|
- the import button is disabled when no project is selected
|
|
- the project card area refreshes after a successful import
|
|
- abstract preview appears only through the delayed preview interaction, not inline by default
|
|
- the item list shows title and year by default, not descendant-wide result sets or collection-level bulk select actions
|