docs: add import floating window design spec

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Saberlve 2026-04-15 15:21:07 +08:00
parent 6003300135
commit 5dee76ecd1

View File

@ -0,0 +1,249 @@
# 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