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 index f9f3292..6da4ee2 100644 --- a/docs/superpowers/specs/2026-04-15-zotero-collection-import-design.md +++ b/docs/superpowers/specs/2026-04-15-zotero-collection-import-design.md @@ -2,43 +2,45 @@ ## Overview -This design upgrades the current left-panel import area from two temporary flows: +This design replaces the current crowded import area with a lighter flow: -- import current selection from a bridge snapshot -- text-search fallback import +- the main page keeps project, cards, and writing views visible +- the import UI moves into a modal opened from a single `导入文献` button -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. +The modal visualizes the user's current Zotero collection hierarchy, lets the user browse collections, displays the literature under the selected collection and all descendants, remembers cross-collection checkbox state for the current browser session, and batch-imports 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 +- Replace the current import widgets with one `导入文献` button that opens an import modal +- Visualize Zotero collections as a tree inside the modal - 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 +- Remember previous selection state while the page remains open, even if the modal is closed and reopened - Support bulk actions: - select all items from the current collection plus descendants - clear all selected items - import selected items into the current project +- Keep the item list compact by default: + - show checkbox and title only + - show abstract only after hover delay - 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 +- 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 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. +The importer is a modal-based subflow launched from the main page. ### Unchanged Areas @@ -58,41 +60,61 @@ The current pair of sections: will be removed and replaced with: -- `从 Zotero 导入` +- one `导入文献` button on the main page +- one `Import From Zotero` modal ## 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 +1. The user selects a project or leaves the page without selecting one yet +2. The user clicks `导入文献` +3. The modal opens +4. The user browses the Zotero collection tree +5. The user clicks one collection +6. The UI loads all literature from that collection and all descendants +7. The user checks 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. -### Left Panel +### Main Page The left panel contains: 1. `创建项目` 2. `项目列表` -3. `从 Zotero 导入` +3. `导入文献` -### Import Section Structure +The collection tree and collection item list are no longer rendered inline on the main page. -The import section has three internal regions: +### Import Modal -1. collection tree -2. collection item list +The modal has three internal regions: + +1. modal header +2. modal body 3. sticky action bar +#### Modal Header + +- title +- current project name or an explicit "未选择项目" state +- selected count +- close button + +#### Modal Body + +- desktop: two-column layout + - left: collection tree + - right: collection item list +- narrow screens: automatically stack into top and bottom sections + #### Collection Tree - tree view with expand/collapse state @@ -107,9 +129,7 @@ The import section has three internal regions: - each row contains: - checkbox - title - - year - - item type - - compact metadata +- year, item type, and abstract are not shown inline by default - rows already selected in other collections remain checked when shown again #### Sticky Action Bar @@ -119,10 +139,28 @@ The import section has three internal regions: - `清空选择` - `导入所选到当前项目` -The action bar remains visible while scrolling within the import section. +The action bar remains visible while scrolling within the modal. ## Interaction Semantics +### Modal Open And Close + +Clicking `导入文献` opens the modal. + +The modal can be closed by: + +- clicking the close button +- clicking the overlay +- 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: @@ -162,26 +200,66 @@ This means: `导入所选到当前项目` 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 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` +- `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 during the current browser session +- `selectedItemKeys` persists across collection switches and modal reopen during the current browser session - `visibleCollectionItems` updates whenever the active collection changes +- `hoveredItemKey`, `hoverTimerId`, and `previewItemKey` control delayed abstract preview `selectedItemKeys` is the source of truth for checkbox rendering and batch import. @@ -256,12 +334,13 @@ The reader should continue to ignore attachment, note, and annotation rows as im ### No Project Selected -- disable import submission or return a clear UI error +- 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 in the import section +- show error state inside the modal - do not affect cards or writing sections ### Empty Collection @@ -276,7 +355,7 @@ The reader should continue to ignore attachment, note, and annotation rows as im ### Missing Local Zotero Data -- show import section unavailable +- disable or error the modal content clearly - preserve the rest of the application ## Testing Strategy @@ -295,7 +374,11 @@ The reader should continue to ignore attachment, note, and annotation rows as im ### UI Tests -The index template must include: +The main page must include: + +- import-modal trigger button + +The modal must include: - collection tree container - collection item list container @@ -304,8 +387,20 @@ The index template must include: - clear-selection button - import-selected button +UI behavior must cover: + +- modal can open without a selected project +- 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 + ## Constraints And Tradeoffs +### Why Modal Instead Of Left-Panel Inline Import + +The inline importer works functionally but makes the main page too crowded. A modal keeps import as a focused subtask and preserves the readability of the project, cards, and writing columns. + ### Why Tree + Side List Instead Of Tree-Mixed Items The user explicitly chose: @@ -315,22 +410,29 @@ The user explicitly chose: This keeps scanning manageable for large libraries and avoids turning the tree itself into an overloaded mixed node structure. +### Why Title-Only Rows By Default + +The user explicitly wanted a lighter list. Showing only the title in the default list keeps density under control, while delayed preview preserves access to abstracts when needed. + ### 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. +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 left panel shows a collection tree and descendant-aware collection item list -- selecting items survives collection switches during the session +- the main page shows a `导入文献` button instead of an inline importer +- clicking the button opens a modal with collection tree and descendant-aware item list +- selecting items survives collection switches and modal reopen 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 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