8.9 KiB
Import Floating Window Design
Overview
This design replaces the current full-screen import modal with a draggable, resizable, minimizable floating window. The user can freely position the literature selector over the main page, minimizing it when not in use, maximizing it when focusing on selection, and closing it without losing context.
What changes: The import experience shifts from a full-screen modal overlay to an independent floating panel.
What stays the same: Collection tree navigation, item browsing, checkbox multi-selection across collections, abstract preview behavior, and batch import flow all remain identical.
Window Attributes
| Attribute | Value |
|---|---|
| Default size | width: 90vw; height: 75vh |
| Default position | Horizontally and vertically centered |
| Minimum size | min-width: 40rem; min-height: 30rem |
| z-index | 40 (same as current modal) |
| Border radius | 16px (rounded corners to distinguish from full-screen modal) |
| Shadow | Enhanced shadow: 0 24px 64px rgba(35, 31, 21, 0.22) |
| Background | var(--surface) |
Window Structure
┌─────────────────────────────────────────────────────────┐
│ [≡] Import From Zotero 当前项目:xxx [_][□][×] │ ← toolbar (draggable)
├──────────────────────┬──────────────────────────────────┤
│ Collections │ Collection Items │
│ ├─ Collection A (5) │ ☑ Title 1 2023 [i] │
│ ├─ Collection B (3) │ ☑ Title 2 2021 [i] │
│ └─ Collection C (8) │ ☐ Title 3 2019 [i] │
│ │ ... │
├──────────────────────┴──────────────────────────────────┤
│ 已选 3 篇 [清空选择] [导入所选] │ ← bottom action bar
└─────────────────────────────────────────────────────────┘
Toolbar (Draggable Header)
- Left: grab handle icon
[≡]+ title "Import From Zotero" + project label - Right: window control buttons
[_][□][×] - Cursor:
grabon toolbar,grabbingwhile dragging - Dragging moves the window by delta from pointer movement
Window Controls
_(Minimize): Reduces window to a small bar at bottom-right of viewport (height: 2.5rem; width: 14rem). The bar shows "导入文献" label +[□]button. Clicking the bar restores the window.□(Maximize): Expands window to100vw × 100vh(same as current modal). Clicking again restores to previous size/position.×(Close): Closes the window and clears session state for the window (position/size/isMinimized/isMaximized).
Bottom Action Bar
- Left: selected count ("已选 N 篇")
- Right: "清空选择" + "导入所选"
- Same behavior as current modal
Internal Layout
display: grid; grid-template-columns: 22rem minmax(0, 1fr); min-height: 0;- Same two-column layout as current modal (collection tree left, items right)
Dragging Behavior
- Drag handle: Only the toolbar area is draggable
- Implementation:
pointerdownon toolbar →pointermoveglobally to updateleft/top→pointerupto end - Boundary constraint:
left >= 0,top >= 0,left + width <= viewport.width,top + height <= viewport.height - Restore on first open: If session state has no saved position, center the window
Minimized State
- Window becomes a thin bar fixed to bottom-right corner of viewport
- Bar contents: "导入文献" text +
[□]restore button - Bar
z-index: 50(above main window and main page elements) - Clicking the bar restores window to last saved size/position
- When restored, window returns to last non-minimized state (position, size, maximized or not)
Maximized State
- Window expands to fill the entire viewport (
width: 100vw; height: 100vh; left: 0; top: 0) - Same layout and behavior as the original full-screen modal
- The
[□]button toggles between maximized and restored state
Session State
Key: zotero-kb.import-window in sessionStorage
{
"x": 120,
"y": 80,
"width": 1100,
"height": 700,
"isMinimized": false,
"isMaximized": false
}
Rules:
- Read on page load; if empty, use default centered position
- Write on every state change (move, resize, minimize, maximize, restore)
- Clear on browser session end
- Window opening behavior: if
isMinimized, show minimized bar; ifisMaximized, show maximized; otherwise restorex, y, width, height
CSS Changes Summary
New Classes
.window-frame {
position: fixed;
z-index: 40;
width: var(--fw-width, 90vw);
height: var(--fw-height, 75vh);
left: var(--fw-left, calc((100vw - var(--fw-width, 90vw)) / 2));
top: var(--fw-top, calc((100vh - var(--fw-height, 75vh)) / 2));
min-width: 40rem;
min-height: 30rem;
border-radius: 16px;
box-shadow: 0 24px 64px rgba(35, 31, 21, 0.22);
display: grid;
grid-template-rows: auto 1fr auto;
overflow: hidden;
background: var(--surface);
}
.window-frame.maximized {
width: 100vw;
height: 100vh;
left: 0;
top: 0;
border-radius: 0;
}
.window-frame.minimized {
display: none;
}
.window-minimized-bar {
position: fixed;
bottom: 1rem;
right: 1rem;
z-index: 50;
height: 2.5rem;
width: 14rem;
border-radius: 8px;
background: var(--surface);
border: 1px solid var(--line);
box-shadow: 0 8px 24px rgba(35, 31, 21, 0.14);
display: flex;
align-items: center;
padding: 0 0.75rem;
gap: 0.5rem;
cursor: pointer;
}
.window-minimized-bar:hover {
background: var(--panel);
}
Removed / Modified
- Remove
.modal-shell,.modal-overlay,.modal-cardfull-screen overlay styles - Replace with
.window-framefor the floating import window
State Keys
| Key | Type | Description |
|---|---|---|
isImportWindowOpen |
boolean | Whether the floating window is open (not minimized) |
windowX, windowY |
number | Pixel position of window top-left |
windowWidth, windowHeight |
number | Pixel dimensions |
isWindowMinimized |
boolean | Whether window is in minimized bar state |
isWindowMaximized |
boolean | Whether window is in full-screen state |
All persisted to sessionStorage key zotero-kb.import-window.
Interaction Changes
| Action | New Behavior |
|---|---|
Click 导入文献 |
Open floating window at saved or default position |
| Drag toolbar | Move window freely within viewport |
Click _ |
Minimize to bottom-right bar |
| Click minimized bar | Restore window |
Click □ |
Toggle maximized (full-screen ↔ restored) |
Click × |
Close window, clear import window session state |
Press Esc |
Same as clicking × (close) |
What Stays The Same
- Collection tree rendering and expand/collapse
- Item list rendering with checkbox, title, year, details button
- Cross-collection selection persistence
- Abstract hover preview (3s delay)
- Selection count, clear, and import actions
- Import API call and success handling (refresh cards, close window)
- Disabled import button when no project is selected
Constraints and Tradeoffs
Why Not Actual window.open?
A native browser window would create separate JavaScript context, requiring postMessage for state synchronization with the main page. A DOM-based floating panel keeps all state in one JavaScript context and is lighter weight.
Why Minimize to Bar, Not Just Hide?
The user wants the literature selector to be accessible without disrupting the main page. A minimize-to-bar pattern (like VSCode) keeps the entry point visible without cluttering the viewport.
Why 90vw × 75vh Default?
This is large enough to browse a collection tree and item list comfortably, while leaving the main page visible on large monitors — supporting the user's workflow of referencing the main page while selecting.
Acceptance Criteria
- Floating window opens at centered 90vw × 75vh on first open
- Window position/size persists across page refreshes in the same session
- Toolbar dragging moves the window freely within viewport bounds
_button minimizes to bottom-right bar; bar click restores□button maximizes to full-screen and restores to previous size×button closes and clears window session stateEsckey closes the window- Main page remains interactive when the floating window is minimized
- All existing import behaviors (collection tree, item selection, import flow) are unchanged