US-017 completed

This commit is contained in:
Roberto Musso
2026-02-22 23:21:37 +01:00
parent 2308158976
commit 98acf6220e
5 changed files with 158 additions and 27 deletions

View File

@@ -295,3 +295,24 @@
- Nord theme (`@milkdown/theme-nord`) provides base ProseMirror structure; override with CSS using the app's semantic color variables for consistent theming
- Import both `@milkdown/theme-nord/style.css` and `@milkdown/kit/prose/view/style/prosemirror.css` for proper base styling
---
## 2026-02-22 - US-017
- What was implemented:
- Fluid Curtain pull-down animation in `AppShell.tsx`
- `framer-motion` `useMotionValue(0)` + `useSpring(y, { stiffness: 300, damping: 30 })` controls `y` CSS transform on a `motion.div` wrapping the content area inside `SidebarInset`
- Sidebar stays visible at all times — only content area slides down
- Trigger 1: `document` wheel event — `findScrollableAncestor()` walks DOM to detect nearest scrollable element; if `scrollTop === 0` and `deltaY < 0`, opens curtain (`y.set(window.innerHeight)`)
- Trigger 2: `Cmd/Ctrl+K` keyboard shortcut toggles curtain open/closed
- Closing: `deltaY > 0` while curtain is open, or `Cmd/Ctrl+K`
- `AIChatPanel` placeholder component at `src/renderer/components/ai/AIChatPanel.tsx` — absolute `z-0` layer behind the sliding content panel
- Right-edge label dynamically changes: `"scrolling up for Adiuva"` + `ChevronUp` when closed, `"back to app"` + `ChevronDown` when open
- App panel remains mounted during animation (no unmount/remount, no state loss)
- `curtainOpenRef` (useRef) keeps event handlers in sync without re-registering effects
- Files changed: `src/renderer/components/layout/AppShell.tsx`, `src/renderer/components/ai/AIChatPanel.tsx` (new), `prd.json`, `progress.txt`
- **Learnings for future iterations:**
- `SidebarInset` already has `relative` in its base classes — adding `overflow-hidden` via className prop is sufficient to clip the sliding `motion.div`
- `useMotionValue` + `useSpring` pattern: `y.set(target)` immediately sets the spring target; `springY` (from `useSpring`) animates toward it — apply `springY` to `style={{ y: springY }}`, not `y` directly
- `findScrollableAncestor()` DOM walk is needed because `body` and `#root` both have `overflow: hidden` — scroll detection must target inner route containers (e.g., `overflow-y-auto` divs in projects/tasks)
- `useRef` for curtain open state avoids stale closures in `useEffect` wheel/keyboard handlers — the boolean ref is updated synchronously alongside `useState` setter
- `{ passive: true }` on wheel listener is correct when not calling `preventDefault()` — avoids Chrome console warnings
---