diff --git a/docs/superpowers/plans/001-floating-save-button.md b/docs/superpowers/plans/001-floating-save-button.md new file mode 100644 index 0000000..4fa18e0 --- /dev/null +++ b/docs/superpowers/plans/001-floating-save-button.md @@ -0,0 +1,132 @@ +# Floating Save Button 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:** Add a floating save button (FAB) to the config page that appears when configuration changes are detected. + +**Architecture:** Inline FAB implementation using framer-motion AnimatePresence for enter/exit animations. Fixed-position circular button with slide-up animation, reusing existing hasChanges state and handleSave function. + +**Tech Stack:** React, framer-motion (v11.18.2), shadcn-bridge/heroui Button, Tailwind CSS + +--- + +## File Structure + +| File | Action | Purpose | +|------|--------|---------| +| `vite-frontend/src/pages/config.tsx` | Modify | Add FAB imports and component at page bottom | + +--- + +### Task 1: Add framer-motion Imports + +**Files:** +- Modify: `vite-frontend/src/pages/config.tsx:1-5` + +- [ ] **Step 1: Add AnimatePresence and motion imports** + +Add import statement after existing framer-motion imports (or at top if none exist). + +Current imports at line 1-2: +```typescript +import { useState, useEffect, useRef } from "react"; +import { useNavigate } from "react-router-dom"; +``` + +Add new import after line 2: +```typescript +import { AnimatePresence, motion } from "framer-motion"; +``` + +- [ ] **Step 2: Commit import addition** + +```bash +git add vite-frontend/src/pages/config.tsx +git commit -m "feat(config): add framer-motion imports for FAB animation" +``` + +--- + +### Task 2: Add FAB Component + +**Files:** +- Modify: `vite-frontend/src/pages/config.tsx:1220-1224` (end of component) + +- [ ] **Step 1: Add FAB at end of component (before closing div)** + +Locate the end of `ConfigPage` component (line ~1223, the closing `` after all modals). + +Insert FAB component before the closing ``: + +```tsx + {/* Floating Save Button (FAB) */} + + {hasChanges && ( + + + + )} + + + ); +} +``` + +- [ ] **Step 2: Run dev server to verify** + +```bash +cd vite-frontend && npm run dev +``` + +Manual verification checklist: +- Open config page at http://localhost:3000/config +- Modify any config field +- Verify FAB appears with slide-up animation +- Click FAB to save +- Verify FAB disappears with slide-down animation after save +- Scroll page and verify FAB stays fixed in viewport corner +- Test on mobile viewport (resize browser or use dev tools) + +- [ ] **Step 3: Commit FAB implementation** + +```bash +git add vite-frontend/src/pages/config.tsx +git commit -m "feat(config): add floating save button (FAB) for issue #266" +``` + +--- + +## Verification Summary + +| Requirement | Verification Method | +|-------------|---------------------| +| FAB hidden by default | Visual: no FAB on page load with no changes | +| FAB appears on change | Visual: modify field → FAB slides up | +| Fixed position | Visual: scroll page → FAB stays in corner | +| Slide-up animation | Visual: observe animation timing/bounce | +| Slide-down on save | Visual: click save → FAB slides down | +| Loading state | Visual: click save → spinner shown during save | +| Mobile compatibility | Visual: resize to mobile viewport → same behavior | + +--- + +## Self-Review Checklist + +- [x] Spec coverage: All requirements from design doc covered (imports + FAB component, animation params, button style, interaction behavior) +- [x] No placeholders: All code shown, no TBD/TODO +- [x] Type consistency: SaveIcon (line 45-59), handleSave (line 372-434), hasChanges (line 214), saving (line 213) all exist in config.tsx \ No newline at end of file diff --git a/docs/superpowers/specs/2026-04-01-floating-save-button-design.md b/docs/superpowers/specs/2026-04-01-floating-save-button-design.md new file mode 100644 index 0000000..1a18f04 --- /dev/null +++ b/docs/superpowers/specs/2026-04-01-floating-save-button-design.md @@ -0,0 +1,162 @@ +# Floating Save Button Design + +**Date:** 2026-04-01 +**Issue:** https://github.com/Sagit-chu/flvx/issues/266 +**Status:** Approved + +## Overview + +Add a Floating Action Button (FAB) to the config page (`vite-frontend/src/pages/config.tsx`) that appears when configuration changes are detected, allowing users to save without scrolling to the top. + +## Requirements + +From Issue #266: + +1. **Default hidden**: FAB not visible when no config changes +2. **Show on change**: Auto-display when `hasChanges` becomes true +3. **Fixed position**: Suspended at bottom-right corner, does not scroll with page +4. **Mobile compatible**: Same behavior on desktop and mobile devices + +## Design Decisions + +### 1. Implementation Approach + +**Inline FAB in config.tsx** (not a reusable component) + +- Rationale: Current need is limited to config page only +- State management (`hasChanges`, `saving`) already exists in the page +- framer-motion patterns already established in project +- Avoids over-abstraction (YAGNI) + +### 2. UI Structure + +Position: `fixed bottom-6 right-6` (24px from viewport edges) + +Visual layout: +``` +┌──────────────────────────────────────┐ +│ [页面内容,可滚动] │ +│ │ +│ [●] │ ← FAB (fixed position) +└──────────────────────────────────────┘ +``` + +### 3. Button Appearance + +- Shape: Circular (`w-12 h-12 rounded-full`) +- Color: Primary (matches existing save button) +- Icon: SaveIcon (already defined in config.tsx) +- Shadow: `shadow-lg` for visual hierarchy +- Style: Icon-only (no text label) + +### 4. Animation + +Using framer-motion with `AnimatePresence`: + +| Phase | Properties | +|-------|------------| +| `initial` | `{ y: 100, opacity: 0 }` - starts below viewport | +| `animate` | `{ y: 0, opacity: 1 }` - slides up to position | +| `exit` | `{ y: 100, opacity: 0 }` - slides back down on hide | + +Transition config: +```typescript +transition={{ type: "spring", damping: 20, stiffness: 300 }} +``` + +Spring parameters produce Material Design-like feel: smooth entrance, slight bounce settle. + +### 5. Interaction Details + +- **Click**: Calls existing `handleSave()` function +- **Loading state**: Button shows Spinner when `saving === true` +- **Hover**: Inherits Button component's primary color hover behavior +- **z-index**: `z-50` (above page content, below modals) +- **Prevent duplicate click**: Button disabled when `saving === true` + +## Technical Implementation + +### Code Location + +File: `vite-frontend/src/pages/config.tsx` + +### Required Imports + +```typescript +import { AnimatePresence, motion } from "framer-motion"; +``` + +### FAB Component Structure + +```tsx + + {hasChanges && ( + + + + )} + +``` + +### Placement + +Insert FAB at the end of the component, before the closing `` (after all Cards and Modals). + +### Dependencies + +- framer-motion: Already installed (v11.18.2) +- Button: Already imported from `@/shadcn-bridge/heroui/button` +- SaveIcon: Already defined in config.tsx + +## Behavior Matrix + +| State | FAB Visibility | Button Enabled | +|-------|----------------|----------------| +| `hasChanges = false` | Hidden (not rendered) | N/A | +| `hasChanges = true, saving = false` | Visible, animating in | Yes | +| `hasChanges = true, saving = true` | Visible | No (loading) | +| Save success | Hidden (animating out) | N/A | + +## Responsive Behavior + +No special handling needed. `fixed bottom-6 right-6` works identically on: +- Desktop browsers +- Mobile browsers +- H5/WebView mode + +The FAB maintains consistent 24px margin from viewport edges regardless of screen size. + +## Edge Cases + +1. **Multiple rapid toggles**: AnimatePresence handles gracefully - exit animation completes before new enter animation +2. **Page unload with unsaved changes**: Not addressed in this design (separate concern) +3. **FAB covers existing warning banner**: z-50 places FAB above the warning banner at line 1004-1013 + +## Testing Checklist + +After implementation, verify: + +- [ ] FAB appears when any config field is modified +- [ ] FAB slides up from bottom on appearance +- [ ] FAB slides down to bottom on disappearance +- [ ] FAB fixed position during page scroll +- [ ] FAB triggers save on click +- [ ] FAB shows spinner during save +- [ ] FAB disappears after successful save +- [ ] FAB works on mobile viewport +- [ ] FAB does not interfere with Modal dialogs \ No newline at end of file diff --git a/vite-frontend/src/pages/config.tsx b/vite-frontend/src/pages/config.tsx index 00af8a1..662fdd8 100644 --- a/vite-frontend/src/pages/config.tsx +++ b/vite-frontend/src/pages/config.tsx @@ -1,5 +1,6 @@ import { useState, useEffect, useRef } from "react"; import { useNavigate } from "react-router-dom"; +import { AnimatePresence, motion } from "framer-motion"; import toast from "react-hot-toast"; import { Button } from "@/shadcn-bridge/heroui/button"; @@ -1220,6 +1221,30 @@ export default function ConfigPage() { )} + + {/* Floating Save Button (FAB) */} + + {hasChanges && ( + + + + )} + ); }