- Added project renaming capability in the workspace with appropriate API endpoints. - Implemented project deletion functionality, ensuring project directories are removed. - Updated UI to support project renaming and deletion, including inline editing and confirmation dialogs. - Enhanced batch generation feature for project items with selection and progress tracking. - Added tests for project renaming and deletion to ensure functionality and integrity.
8.9 KiB
Import Window Responsive Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Make the floating import window recompute a viewport-appropriate size and position every time it opens, and stack collection/item panes vertically on narrower viewports.
Architecture: Keep the existing floating-window shell and collection importer flow, but replace reopen-time geometry reuse with viewport-derived geometry helpers. Add a narrow-layout breakpoint in the template CSS and a resize handler that constrains normal-mode geometry without overriding minimized or maximized state.
Tech Stack: FastAPI template rendering, inline HTML/CSS/JavaScript, pytest, Node syntax checking
Task 1: Update UI Contract Tests For Responsive Window Behavior
Files:
-
Modify:
tests/test_ui.py -
Test:
tests/test_ui.py -
Step 1: Write the failing test
def test_index_has_responsive_import_window_geometry_helpers(tmp_path) -> None:
html = _get_index_html(tmp_path)
assert "function computeResponsiveWindowRect()" in html
assert "function applyResponsiveWindowRect()" in html
assert "function syncWindowToViewport()" in html
assert "window.addEventListener(\"resize\"" in html
def test_index_has_responsive_import_window_layout_rules(tmp_path) -> None:
html = _get_index_html(tmp_path)
assert ".window-pane.items" in html
assert "@media (max-width: 980px)" in html
assert ".window-body {" in html
assert "grid-template-columns: 1fr;" in html
assert "border-bottom: 1px solid var(--line);" in html
- Step 2: Run test to verify it fails
Run: UV_CACHE_DIR=/tmp/uv-cache uv run pytest tests/test_ui.py -q
Expected: FAIL because the new responsive geometry helper names are not present yet.
- Step 3: Keep the existing syntax guard test
Do not remove test_index_inline_script_is_valid_javascript; it remains the regression guard for the inline script.
- Step 4: Run test to verify the contract file still parses
Run: UV_CACHE_DIR=/tmp/uv-cache uv run pytest tests/test_ui.py::test_index_inline_script_is_valid_javascript -q
Expected: PASS
- Step 5: Commit
git add tests/test_ui.py
git commit -m "test: define responsive import window contract"
Task 2: Implement Viewport-Derived Window Geometry
Files:
-
Modify:
src/zotero_kb/templates/index.html -
Test:
tests/test_ui.py -
Step 1: Write the failing test
Use the Task 1 tests as the failing contract. Do not add production code first.
- Step 2: Run the failing test
Run: UV_CACHE_DIR=/tmp/uv-cache uv run pytest tests/test_ui.py -q
Expected: FAIL with missing responsive helper function names or missing layout assertions.
- Step 3: Write minimal implementation
Add responsive geometry helpers to the inline script in src/zotero_kb/templates/index.html:
function computeResponsiveWindowRect() {
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
const margin = viewportWidth <= 980 ? 8 : 24;
const minWidth = viewportWidth <= 980 ? 320 : 640;
const minHeight = viewportHeight <= 980 ? 420 : 480;
const maxWidth = Math.max(minWidth, viewportWidth - margin * 2);
const maxHeight = Math.max(minHeight, viewportHeight - margin * 2);
const width = Math.min(maxWidth, Math.max(minWidth, Math.round(viewportWidth * 0.88)));
const height = Math.min(maxHeight, Math.max(minHeight, Math.round(viewportHeight * 0.82)));
const left = Math.max(margin, Math.round((viewportWidth - width) / 2));
const top = Math.max(margin, Math.round((viewportHeight - height) / 2));
return { width, height, left, top };
}
function applyResponsiveWindowRect() {
const fw = elements.importWindow;
if (!fw || state.isWindowMinimized || state.isWindowMaximized) return;
const rect = computeResponsiveWindowRect();
fw.style.width = `${rect.width}px`;
fw.style.height = `${rect.height}px`;
fw.style.left = `${rect.left}px`;
fw.style.top = `${rect.top}px`;
state.windowWidth = rect.width;
state.windowHeight = rect.height;
state.windowX = rect.left;
state.windowY = rect.top;
}
function syncWindowToViewport() {
const fw = elements.importWindow;
if (!fw || state.isWindowMinimized || state.isWindowMaximized) return;
const margin = window.innerWidth <= 980 ? 8 : 24;
const rect = computeResponsiveWindowRect();
const currentWidth = parseFloat(fw.style.width) || rect.width;
const currentHeight = parseFloat(fw.style.height) || rect.height;
const width = Math.min(currentWidth, rect.width);
const height = Math.min(currentHeight, rect.height);
const maxLeft = Math.max(margin, window.innerWidth - width - margin);
const maxTop = Math.max(margin, window.innerHeight - height - margin);
const left = Math.min(Math.max(parseFloat(fw.style.left) || rect.left, margin), maxLeft);
const top = Math.min(Math.max(parseFloat(fw.style.top) || rect.top, margin), maxTop);
fw.style.width = `${width}px`;
fw.style.height = `${height}px`;
fw.style.left = `${left}px`;
fw.style.top = `${top}px`;
state.windowWidth = width;
state.windowHeight = height;
state.windowX = left;
state.windowY = top;
}
Update openImportWindow() to call applyResponsiveWindowRect() after initWindowFromSession() when the window is not minimized or maximized.
- Step 4: Run the focused tests
Run: UV_CACHE_DIR=/tmp/uv-cache uv run pytest tests/test_ui.py -q
Expected: PASS
- Step 5: Commit
git add src/zotero_kb/templates/index.html tests/test_ui.py
git commit -m "feat: derive import window geometry from viewport"
Task 3: Add Responsive Layout Switching And Resize Wiring
Files:
-
Modify:
src/zotero_kb/templates/index.html -
Test:
tests/test_ui.py -
Step 1: Write the failing test
Use the layout assertions from Task 1 as the failing contract. No new production code before running them.
- Step 2: Run the failing test
Run: UV_CACHE_DIR=/tmp/uv-cache uv run pytest tests/test_ui.py -q
Expected: FAIL if the resize listener or stacked layout rules are still missing.
- Step 3: Write minimal implementation
Extend the existing media query and event wiring in src/zotero_kb/templates/index.html:
@media (max-width: 980px) {
.window-frame {
min-width: 0;
width: calc(100vw - 1rem);
height: calc(100vh - 1rem);
left: 0.5rem;
top: 0.5rem;
}
.window-toolbar,
.window-toolbar-actions {
align-items: stretch;
}
.window-body {
grid-template-columns: 1fr;
}
.window-pane.collections {
border-right: 0;
border-bottom: 1px solid var(--line);
}
}
Wire the resize handler:
window.addEventListener("resize", () => {
syncWindowToViewport();
});
- Step 4: Run the focused tests
Run: UV_CACHE_DIR=/tmp/uv-cache uv run pytest tests/test_ui.py -q
Expected: PASS
- Step 5: Commit
git add src/zotero_kb/templates/index.html tests/test_ui.py
git commit -m "feat: make import window layout responsive"
Task 4: End-To-End Verification
Files:
-
Modify: none
-
Test:
tests/test_ui.py,tests/test_api.py -
Step 1: Run the UI test suite
Run: UV_CACHE_DIR=/tmp/uv-cache uv run pytest tests/test_ui.py -q
Expected: PASS with all UI contract tests green.
- Step 2: Run the API regression suite
Run: UV_CACHE_DIR=/tmp/uv-cache uv run pytest tests/test_api.py -q
Expected: PASS with all API tests green.
- Step 3: Run fresh JS syntax verification through pytest
Run: UV_CACHE_DIR=/tmp/uv-cache uv run pytest tests/test_ui.py::test_index_inline_script_is_valid_javascript -q
Expected: PASS
- Step 4: Manual verification
Run the app:
UV_CACHE_DIR=/tmp/uv-cache uv run python main.py
Manual checks:
-
open the import window at one browser zoom level and note the size
-
close it, change browser zoom, reopen it, confirm it recenters and resizes
-
shrink the viewport below the narrow breakpoint, reopen it, confirm the panes stack vertically
-
maximize and minimize the window, confirm those modes still behave correctly
-
Step 5: Commit
git add src/zotero_kb/templates/index.html tests/test_ui.py docs/superpowers/specs/2026-04-16-import-window-responsive-design.md docs/superpowers/plans/2026-04-16-import-window-responsive.md
git commit -m "feat: make import window adapt to viewport changes"