# 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: `grab` on toolbar, `grabbing` while 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 to `100vw × 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:** `pointerdown` on toolbar → `pointermove` globally to update `left`/`top` → `pointerup` to 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` ```json { "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; if `isMaximized`, show maximized; otherwise restore `x, y, width, height` --- ## CSS Changes Summary ### New Classes ```css .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-card` full-screen overlay styles - Replace with `.window-frame` for 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 state - [ ] `Esc` key 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