US-017 completed
This commit is contained in:
21
progress.txt
21
progress.txt
@@ -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
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user