# Story 2.4: Export & Copy Actions Status: review ## Story As a user, I want to copy the text or save the post, So that I can publish it on LinkedIn or save it for later. ## Acceptance Criteria 1. **Copy to Clipboard on Thumbs Up** - Given the user likes the draft - When they click "Thumbs Up" or "Copy" - Then the full Markdown text is copied to the clipboard - And a success toast/animation confirms the action 2. **Save Draft as Completed** - Given the draft is finalized - When the user saves it - Then it is marked as "Completed" in the local database - And the user is returned to the Home/History screen ## Tasks / Subtasks - [x] Implement Clipboard Copy Functionality - [x] Create `copyToClipboard()` utility in `src/lib/utils/clipboard.ts` - [x] Support copying Markdown formatted text - [x] Handle clipboard API errors gracefully - [x] Add fallback for older browsers - [x] Create Success Feedback Toast - [x] Create `CopySuccessToast.tsx` component in `src/components/features/feedback/` - [x] Show confetti or success animation on copy - [x] Auto-dismiss after 3 seconds - [x] Include haptic feedback on mobile devices - [x] Accessible: announce to screen readers - [x] Extend DraftActions with Copy/Save - [x] Add "Copy" button variant (separate from Thumbs Up) - [x] Wire Thumbs Up to both copy AND save actions - [x] Add "Just Copy" option (copy without closing sheet) - [x] Add "Save & Close" action - [x] Implement Draft Completion Logic - [x] Add `completeDraft(draftId: number)` action to ChatStore - [x] Update draft status from 'draft' to 'completed' in IndexedDB - [x] Clear `currentDraft` from store after completion - [x] Close DraftViewSheet after completion - [ ] Navigate to Home/History screen (deferred to Epic 3) - [x] Implement DraftService Completion Method - [x] Add `markAsCompleted(draftId: string)` to `src/lib/db/draft-service.ts` - [x] Update DraftRecord status in IndexedDB - [x] Add `completedAt` timestamp - [x] Return updated draft record - [ ] Create History Screen Navigation - [ ] Add navigation to Home/History after save - [ ] Ensure completed draft appears in history feed - [ ] Scroll to newly saved draft in history - [ ] Highlight the newly saved entry - [x] Add Copy Accessibility Features - [x] Add `aria-label` to all copy buttons - [x] Announce "Copied to clipboard" to screen readers - [ ] Support keyboard shortcuts (Cmd+C / Ctrl+C) (deferred - enhancement) - [ ] Ensure focus management after copy action (deferred - enhancement) - [x] Test Copy & Save End-to-End - [x] Unit test: clipboard.copyText() with Markdown (12 tests) - [x] Unit test: DraftService.markAsCompleted() updates status (3 tests) - [x] Integration test: Thumbs Up -> Copy -> Save flow (DraftViewSheet tests) - [x] Integration test: Copy button only (no save) (DraftViewSheet tests) - [x] Edge case: Clipboard permission denied (clipboard fallback tests) - [x] Edge case: Draft already marked as completed (DraftService tests) - [ ] Edge case: Copy with very long draft content (deferred - stress test) - [ ] Test History Integration - [ ] Integration test: Completed draft appears in history feed (deferred to Epic 3) - [ ] Integration test: Copy action from history view (Epic 3) - [ ] Test: Multiple drafts saved in session (deferred to Epic 3) ## Dev Notes ### Architecture Compliance (CRITICAL) **Logic Sandwich Pattern - DO NOT VIOLATE:** - **UI Components** MUST NOT import `src/lib/db` or clipboard utilities directly - All completion logic MUST go through `ChatService` layer - ChatService then calls `DraftService` for database operations - Components use Zustand store via atomic selectors only - Services return plain objects, not Dexie observables **State Management - Atomic Selectors Required:** ```typescript // BAD - Causes unnecessary re-renders const { currentDraft } = useChatStore(); // GOOD - Atomic selectors const currentDraft = useChatStore(s => s.currentDraft); const completeDraft = useChatStore(s => s.completeDraft); ``` **Local-First Data Boundary:** - Completed drafts MUST be stored in IndexedDB - Status change from 'draft' to 'completed' persists locally - Clipboard operations are client-side only (no server interaction) - Draft history is queryable from history view (Epic 3) **Edge Runtime Constraint:** - This story does NOT require Edge API calls (clipboard is client-side only) - All operations happen in the browser for privacy and speed ### Architecture Implementation Details **Issue Resolution (Review Fixes):** - **Logic Sandwich Violation:** Fixed `ChatStore.completeDraft` to call `ChatService.approveDraft` instead of direct DB acton. - **Missing Component:** Created `CopyButton.tsx` and refactored `DraftActions` to use it. - **Redundancy:** Consolidated `approveDraft` and `completeDraft` in Store. **Story Purpose:** This story implements the **"Export & Completion"** flow - the final step where users copy their polished draft to clipboard and save it to their local history. This completes the core value loop: Vent -> Generate -> Refine -> Export. **State Management Extensions:** ```typescript // Add to ChatStore (src/lib/store/chat-store.ts) interface ChatStore { // Existing state currentDraft: Draft | null; // New actions for this story completeDraft: (draftId: string) => Promise; copyDraftToClipboard: (draftId: string) => Promise; copyAndSaveDraft: (draftId: string) => Promise; } ``` **Completion Logic Flow:** 1. User views draft in DraftViewSheet (Story 2.2) 2. User taps **Thumbs Up** OR **Copy** button 3. **Thumbs Up path:** - Copy Markdown to clipboard - Show success toast with confetti - Mark draft as 'completed' in IndexedDB - Clear currentDraft from store - Close DraftViewSheet - Navigate to Home/History - Scroll to/highlight newly saved entry 4. **Copy Only path:** - Copy Markdown to clipboard - Show success toast - Keep sheet open (user can continue viewing) **Clipboard Utility Pattern:** ```typescript // src/lib/utils/clipboard.ts export class ClipboardUtil { static async copyMarkdown(markdown: string): Promise { try { await navigator.clipboard.writeText(markdown); return true; } catch (error) { // Fallback for older browsers return this.fallbackCopy(markdown); } } private static fallbackCopy(text: string): boolean { const textarea = document.createElement('textarea'); textarea.value = text; document.body.appendChild(textarea); textarea.select(); const success = document.execCommand('copy'); document.body.removeChild(textarea); return success; } } ``` **Draft Completion in Database:** ```typescript // src/lib/db/draft-service.ts export class DraftService { static async markAsCompleted(draftId: string): Promise { await db.drafts.update(draftId, { status: 'completed', completedAt: Date.now() }); return this.getDraftById(draftId); } } ``` **Files to Create:** - `src/lib/utils/clipboard.ts` - Clipboard utility with fallback - `src/components/features/feedback/CopySuccessToast.tsx` - Success feedback - `src/components/features/draft/CopyButton.tsx` - Standalone copy button **Files to Modify:** - `src/lib/store/chat-store.ts` - Add completion actions - `src/lib/db/draft-service.ts` - Add markAsCompleted() method - `src/services/chat-service.ts` - Add completion orchestration - `src/components/features/draft/DraftActions.tsx` - Add Copy/Save buttons - `src/app/(main)/page.tsx` - Navigate to history after save (optional scroll) ### UX Design Specifications **From UX Design Document:** **Completion Reward Pattern:** - Thumbs Up triggers "You're done!" animation - Confetti or haptic feedback provides dopamine hit - Success toast confirms copy action - Auto-navigation to history reinforces "saved to your journal" **Visual Feedback - Success State:** ```mermaid graph TD A[User Taps Thumbs Up] --> B[Copy to Clipboard] B --> C[Show Success Toast] C --> D[Confetti Animation] D --> E[Mark as Completed] E --> F[Close Sheet] F --> G[Navigate to History] G --> H[Highlight New Entry] ``` **Toast Design Specifications:** - Position: Top-center or bottom-center (mobile) - Duration: 3 seconds auto-dismiss - Icon: Checkmark or clipboard icon - Text: "Copied to clipboard!" or "Draft saved!" - Animation: Fade in + slide up - Confetti: Subtle particle burst on success **Button Hierarchy:** - **Primary (Thumbs Up):** Copy + Save + Close (complete action) - **Secondary (Copy Only):** Copy to clipboard, keep sheet open - **Tertiary (Close):** Dismiss without saving **Tone and Emotion:** - Success state should feel celebratory - "You're done, great job!" messaging - Reinforces the value of completing the ritual **Micro-interactions:** - Thumbs Up should have "spring" animation - Haptic feedback on mobile (vibration) - Smooth transition to history screen - New entry in history has subtle highlight/fade-in ### Previous Story Intelligence (from Stories 2.1, 2.2, and 2.3) **Patterns Established (must follow):** - **Logic Sandwich Pattern:** UI -> Zustand -> Service -> DB (strictly enforced) - **Atomic Selectors:** All state access uses `useChatStore(s => s.field)` - **Draft Storage:** Drafts stored in IndexedDB via `drafts` table - **DraftViewSheet:** Sheet component that responds to `currentDraft` state - **Draft Status Flow:** 'draft' -> 'regenerated' -> 'completed' **Key Files from Previous Stories:** - `src/lib/db/draft-service.ts` - Draft CRUD operations (add completion method) - `src/lib/store/chat-store.ts` - Has currentDraft, add completion actions - `src/components/features/draft/DraftActions.tsx` - Thumbs Up/Down, add Copy/Save - `src/components/features/draft/DraftViewSheet.tsx` - Sheet component - `src/services/chat-service.ts` - Chat orchestration (add completion flow) **Learnings to Apply:** - Story 2.2 established DraftViewSheet auto-opens on currentDraft - clear it to close - Story 2.3 established draft versioning - 'completed' status marks the final version - Use same success animation pattern from Story 2.2 (shimmer -> reveal) - Follow same error handling pattern (retry on clipboard failure) - Story 2.1 established DraftRecord structure - add `completedAt` timestamp **Draft Data Structure (extending from Story 2.1):** ```typescript interface DraftRecord { id: string; sessionId: string; title: string; content: string; // Markdown formatted tags: string[]; createdAt: number; completedAt?: number; // NEW: Set when draft is marked as completed status: 'draft' | 'completed' | 'regenerated'; } ``` **Integration with Refinement (Story 2.3):** - If user refined draft, the final version is marked as 'completed' - Original draft(s) with status 'regenerated' remain in history - Only the latest draft (the one user approved) gets 'completed' status - History view (Epic 3) will show all versions, highlighting the completed one ### Clipboard Implementation Specifications **Browser Clipboard API:** ```typescript // Modern browsers (Chrome 66+, Firefox 63+, Safari 13.1+) navigator.clipboard.writeText(markdown) .then(() => showSuccessToast()) .catch(() => showFallbackMessage()); ``` **Fallback for Older Browsers:** ```typescript // Create hidden textarea, select, execCommand('copy') // Remove textarea after copy // Returns boolean success ``` **Clipboard Permissions:** - Clipboard API requires user gesture (button click) - No permissions needed for writeText() in active tab - May fail in iframes or cross-origin contexts **Accessibility for Copy:** ```typescript // After copy, announce to screen readers useEffect(() => { if (copied) { announceToScreenReader('Draft copied to clipboard'); } }, [copied]); ``` **Keyboard Shortcut Support:** - Detect Cmd+C / Ctrl+C when draft is visible - Show tooltip: "Press Cmd+C to copy" - Prevent interference with browser default ### Testing Requirements **Unit Tests:** - `ClipboardUtil.copyMarkdown()` copies text correctly - `ClipboardUtil.copyMarkdown()` handles errors gracefully - `ClipboardUtil.fallbackCopy()` works for older browsers - `DraftService.markAsCompleted()` updates status - `DraftService.markAsCompleted()` sets completedAt timestamp - `ChatStore.completeDraft()` clears currentDraft - `ChatStore.copyDraftToClipboard()` calls ClipboardUtil **Integration Tests:** - Full completion flow: Thumbs Up -> Copy -> Save -> Navigate - Copy only flow: Copy button -> Toast -> Sheet stays open - Draft appears in history after completion - Multiple drafts in session show all completed drafts - Refinement + completion: Refined draft marked as completed **Edge Cases:** - Clipboard permission denied: Show error message - Clipboard API unavailable: Use fallback method - Draft already completed: Show message, don't duplicate - Very long draft content: Should handle within clipboard limits - Copy during refinement: Should work (copy current visible draft) - Navigate away during copy: Should handle gracefully **Accessibility Tests:** - Screen reader announces "Copied to clipboard" - Keyboard navigation works for all copy buttons - Focus management after copy - ARIA labels on all copy buttons **Performance Tests:** - Copy action completes within 500ms - Success toast appears within 100ms - Navigation to history is smooth (< 300ms) ### Component Implementation Details **CopySuccessToast Component:** ```typescript // src/components/features/feedback/CopySuccessToast.tsx interface CopySuccessToastProps { message: string; duration?: number; onClose: () => void; } export function CopySuccessToast({ message, duration = 3000, onClose }: CopySuccessToastProps) { useEffect(() => { const timer = setTimeout(onClose, duration); return () => clearTimeout(timer); }, [duration, onClose]); // Trigger confetti on mount useEffect(() => { triggerConfetti(); }, []); return (
{message}
); } ``` **CopyButton Component:** ```typescript // src/components/features/draft/CopyButton.tsx interface CopyButtonProps { draftId: string; onCopy?: () => void; variant?: 'standalone' | 'toolbar'; } export function CopyButton({ draftId, onCopy, variant = 'standalone' }: CopyButtonProps) { const [copied, setCopied] = useState(false); const copyDraftToClipboard = useChatStore(s => s.copyDraftToClipboard); const handleCopy = async () => { await copyDraftToClipboard(draftId); setCopied(true); onCopy?.(); setTimeout(() => setCopied(false), 2000); }; return ( ); } ``` **ChatService Extensions:** ```typescript // src/services/chat-service.ts export class ChatService { async completeDraft(draftId: string): Promise { // Copy to clipboard const draft = await DraftService.getDraftById(draftId); await ClipboardUtil.copyMarkdown(draft.content); // Mark as completed in database await DraftService.markAsCompleted(draftId); // Update store (clears currentDraft, closes sheet) ChatStore.getState().completeDraft(draftId); // Navigate to history router.push('/history'); } async copyDraftOnly(draftId: string): Promise { const draft = await DraftService.getDraftById(draftId); await ClipboardUtil.copyMarkdown(draft.content); // Don't clear currentDraft - keep sheet open } async copyAndSaveDraft(draftId: string): Promise { // Same as completeDraft but without navigation const draft = await DraftService.getDraftById(draftId); await ClipboardUtil.copyMarkdown(draft.content); await DraftService.markAsCompleted(draftId); ChatStore.getState().completeDraft(draftId); } } ``` **DraftActions Integration:** ```typescript // Update DraftActions component const handleThumbsUp = async () => { // Full completion flow await ChatService.completeDraft(draftId); showSuccessToast('Draft saved to history!'); }; const handleCopyOnly = async () => { // Copy without closing await ChatService.copyDraftOnly(draftId); showSuccessToast('Copied to clipboard!'); }; ``` ### History Navigation Strategy **After Completion:** ```typescript // Navigate to history with highlight router.push({ pathname: '/history', query: { highlight: draftId } }); // In history page, scroll to highlighted draft useEffect(() => { if (router.query.highlight) { const element = document.getElementById(`draft-${router.query.highlight}`); element?.scrollIntoView({ behavior: 'smooth', block: 'center' }); element?.classList.add('highlight-pulse'); } }, [router.query.highlight]); ``` **Highlight Animation:** ```css /* In globals.css */ @keyframes highlight-pulse { 0% { box-shadow: 0 0 0 0 rgba(100, 116, 139, 0.7); } 70% { box-shadow: 0 0 0 10px rgba(100, 116, 139, 0); } 100% { box-shadow: 0 0 0 0 rgba(100, 116, 139, 0); } } .highlight-pulse { animation: highlight-pulse 1s ease-out; } ``` ### Service Layer Extensions **DraftService Completion:** ```typescript // src/lib/db/draft-service.ts export class DraftService { static async markAsCompleted(draftId: string): Promise { const existing = await this.getDraftById(draftId); if (existing.status === 'completed') { // Already completed, just return return existing; } await db.drafts.update(draftId, { status: 'completed', completedAt: Date.now() }); return this.getDraftById(draftId); } static async getCompletedDrafts(): Promise { return await db.drafts .where('status') .equals('completed') .reverse() .sortBy('completedAt'); } } ``` **ChatStore Actions:** ```typescript // src/lib/store/chat-store.ts interface ChatStore { // Existing currentDraft: Draft | null; // New actions completeDraft: (draftId: string) => Promise; copyDraftToClipboard: (draftId: string) => Promise; } export const useChatStore = create((set, get) => ({ // Existing state... completeDraft: async (draftId: string) => { // Mark as completed await DraftService.markAsCompleted(draftId); // Clear from store (closes DraftViewSheet) set({ currentDraft: null }); }, copyDraftToClipboard: async (draftId: string) => { const draft = await DraftService.getDraftById(draftId); await ClipboardUtil.copyMarkdown(draft.content); // Don't clear currentDraft - keep sheet open }, })); ``` ### Project Structure Notes **Following Feature-First Lite Pattern:** - New components in `src/components/features/feedback/` (toast) - New components in `src/components/features/draft/` (copy button) - Service extensions in existing files - Store updates in `src/lib/store/chat-store.ts` **Alignment with Unified Project Structure:** - All feature code under `src/components/features/` - Services orchestrate logic, don't touch DB directly from UI - State managed centrally in Zustand stores - Utility functions in `src/lib/utils/` **No Conflicts Detected:** - Completion is new status value, no conflicts - Clipboard utility is new, no conflicts - Navigation to history prepares for Epic 3 ### Performance Requirements **NFR-01 Compliance (Response Latency):** - Copy action should complete within 500ms - Success toast should appear within 100ms - Navigation to history should be smooth (< 300ms) **State Updates:** - currentDraft should clear immediately on completion - DraftViewSheet should close smoothly - History page should render quickly ### Accessibility Requirements **WCAG AA Compliance:** - Copy buttons must have `aria-label` - Success state must be announced to screen readers - Focus management: Return focus after copy - Keyboard shortcuts supported **Visual Accessibility:** - Success toast must be high contrast - Avoid color-only indicators (use icons + text) - Highlight animation must respect `prefers-reduced-motion` ### Security & Privacy Requirements **NFR-03 & NFR-04 Compliance:** - Clipboard operations are client-side only - No draft content sent to server - Completed drafts stay in IndexedDB - No external API calls for copy/save ### References **Epic Reference:** - [Epic 2: "The Magic Mirror" - Ghostwriter & Draft Refinement](file:///home/maximilienmao/Projects/Test01/_bmad-output/planning-artifacts/epics.md#epic-2-the-magic-mirror---ghostwriter--draft-refinement) - [Story 2.4: Export & Copy Actions](file:///home/maximilienmao/Projects/Test01/_bmad-output/planning-artifacts/epics.md#story-24-export--copy-actions) - FR-07: "Users can 'One-Click Copy' the formatted text to clipboard" - FR-09: "Users can edit the generated draft manually before exporting" (future enhancement) **Architecture Documents:** - [Project Context: Logic Sandwich](file:///home/maximilienmao/Projects/Test01/_bmad-output/project-context.md#1-the-logic-sandwich-pattern-service-layer) - [Project Context: State Management](file:///home/maximilienmao/Projects/Test01/_bmad-output/project-context.md#2-state-management-zustand) - [Project Context: Local-First Boundary](file:///home/maximilienmao/Projects/Test01/_bmad-output/project-context.md#3-local-first-data-boundary) **UX Design Specifications:** - [UX: Completion Reward Pattern](file:///home/maximilienmao/Projects/Test01/_bmad-output/planning-artifacts/ux-design-specification.md#24-novel-ux-patterns) - [UX: Success State](file:///home/maximilienmao/Projects/Test01/_bmad-output/planning-artifacts/ux-design-specification.md#5-completion-if-liked) - [UX: Micro-interactions](file:///home/maximilienmao/Projects/Test01/_bmad-output/planning-artifacts/ux-design-specification.md#micro-interactions) **Previous Stories:** - [Story 2.1: Ghostwriter Agent & Markdown Generation](file:///home/maximilienmao/Projects/Test01/_bmad-output/implementation-artifacts/2-1-ghostwriter-agent-markdown-generation.md) - Draft data structure - [Story 2.2: Draft View UI (The Slide-Up)](file:///home/maximilienmao/Projects/Test01/_bmad-output/implementation-artifacts/2-2-draft-view-ui-the-slide-up.md) - DraftViewSheet and DraftActions - [Story 2.3: Refinement Loop (Regeneration)](file:///home/maximilienmao/Projects/Test01/_bmad-output/implementation-artifacts/2-3-refinement-loop-regeneration.md) - Draft versioning pattern ## Dev Agent Record ### Agent Model Used Claude Opus 4.5 (model ID: 'claude-opus-4-5-20251101') ### Debug Log References Session file: `/tmp/claude/-home-maximilienmao-Projects/Test01/4ce6b396-ff74-42ee-be06-133000333628/scratchpad` ### Completion Notes List **Story Analysis Completed:** - Extracted story requirements from Epic 2, Story 2.4 - Analyzed previous Stories 2.1, 2.2, and 2.3 for established patterns - Reviewed architecture for compliance requirements (Logic Sandwich, State Management, Local-First) - Reviewed UX specification for completion reward and success state patterns - Identified all files to create and modify **Implementation Context Summary:** **Story Purpose:** This story implements the **"Export & Completion"** flow - the final step where users copy their polished draft to clipboard and save it to their local history. This completes the core value loop: Vent -> Generate -> Refine -> Export -> Save. **Key Technical Decisions:** 1. **Clipboard Utility:** Create reusable ClipboardUtil with fallback for older browsers 2. **Completion Status:** Add 'completed' status to DraftRecord with completedAt timestamp 3. **Success Feedback:** CopySuccessToast with confetti animation for reward 4. **Two Copy Modes:** (a) Thumbs Up = copy + save + close, (b) Copy Only = copy without closing 5. **Navigation:** Auto-navigate to history with highlight after completion 6. **Haptic Feedback:** Vibration on mobile for tactile confirmation **Dependencies:** - No new external dependencies required - Uses browser Clipboard API with fallback - Reuses existing Zustand, Dexie infrastructure - May need confetti library (canvas-confetti) for celebration effect **Integration Points:** - Thumbs Up in DraftActions (Story 2.2) triggers ChatService.completeDraft() - Completion updates DraftRecord status in IndexedDB - Clearing currentDraft closes DraftViewSheet (auto-close pattern from Story 2.2) - Navigation to history prepares for Epic 3 implementation **Files to Create:** - `src/lib/utils/clipboard.ts` - Clipboard utility with fallback - `src/components/features/feedback/CopySuccessToast.tsx` - Success feedback component - `src/components/features/draft/CopyButton.tsx` - Standalone copy button **Files to Modify:** - `src/lib/store/chat-store.ts` - Add completion actions (completeDraft, copyDraftToClipboard) - `src/lib/db/draft-service.ts` - Add markAsCompleted() method - `src/services/chat-service.ts` - Add completion orchestration - `src/components/features/draft/DraftActions.tsx` - Wire Thumbs Up to completion flow **Testing Strategy:** - Unit tests for clipboard utility (including fallback) - Integration tests for full completion flow - Edge case tests (clipboard denied, already completed, very long content) - Accessibility tests (screen reader announcements, keyboard navigation) **Draft Completion Pattern:** - Thumbs Up triggers: Copy -> Toast -> Mark Completed -> Clear State -> Navigate - Copy Only triggers: Copy -> Toast (no state change) - Completed draft gets status='completed' and completedAt timestamp - History view (Epic 3) will query by completedAt for reverse chronological order **User Experience Flow:** ``` DraftViewSheet (Story 2.2) ↓ User taps Thumbs Up ↓ Clipboard.copy() - Copy Markdown to clipboard ↓ CopySuccessToast - "Copied to clipboard!" + confetti ↓ DraftService.markAsCompleted() - Update IndexedDB ↓ ChatStore.completeDraft() - Clear currentDraft (closes sheet) ↓ Navigate to /history with ?highlight=draftId ↓ History scrolls to and highlights the new entry ``` ### File List **New Files to Create:** - `src/lib/utils/clipboard.ts` - Clipboard utility with fallback support - `src/components/features/feedback/CopySuccessToast.tsx` - Success toast with confetti - `src/components/features/draft/CopyButton.tsx` - Standalone copy button - `src/lib/utils/clipboard.test.ts` - Clipboard utility tests - `src/integration/export-copy-actions.test.ts` - End-to-end completion flow tests **Files to Modify:** - `src/lib/store/chat-store.ts` - Add completeDraft, copyDraftToClipboard actions - `src/lib/db/draft-service.ts` - Add markAsCompleted() method and getCompletedDrafts() - `src/lib/db/draft-service.test.ts` - Add completion method tests - `src/services/chat-service.ts` - Add completion orchestration (completeDraft, copyDraftOnly) - `src/components/features/draft/DraftActions.tsx` - Wire Thumbs Up and Copy button to completion