# Tree Fork UI Implementation Plan > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. **Goal:** Add an explicit `ForkModal` with a "Reason for Forking" field to replace the silent fork flow, and show a "Fork" chip badge on forked tree cards in the library and My Trees views. **Architecture:** The backend is fully complete (POST `/trees/:id/fork` accepts `{ name, fork_reason }`). The frontend `treesApi.fork()` already accepts these params. We need: (1) `ForkInfo` types added to `tree.ts`, (2) a new `ForkModal` component, (3) updated fork handlers in `TreeLibraryPage` and `MyTreesPage` to open the modal instead of forking silently, (4) a "Fork" chip in all three card views (grid, list, table). **Tech Stack:** React 19, TypeScript, Tailwind CSS, Lucide React, `treesApi.fork(id, { name, fork_reason })` already wired. --- ## Context for the Implementer - `treesApi.fork(id, data?)` is at `frontend/src/api/trees.ts:42` — already accepts `{ fork_reason?, name? }` - `onForkTree` prop exists on all three card views and currently passes only `treeId: string` - `TreeLibraryPage` has `handleForkTree(treeId: string)` at line ~247 that calls `treesApi.fork(treeId)` silently - `MyTreesPage` does NOT currently have a fork handler — the "Fork" UI there is an informational message (line ~215), not a button wired to `onForkTree` - `TreeListItem` (used by all three views) does NOT yet have `fork_depth` or `parent_tree_id` — must add these - `MyTreesPage` already uses `tree.parent_tree_id` at line ~283 for a "Forked from" display block — this field must be on the type for that to compile cleanly after our changes - All three card views are in `frontend/src/components/library/` - Design system: `bg-violet-400/15 text-violet-400` for the Fork chip; `bg-gradient-brand` for the Fork submit button; modal structure uses `bg-card border-border rounded-xl` --- ### Task 1: Add `ForkInfo` type and fork fields to `TreeListItem` and `Tree` **Files:** - Modify: `frontend/src/types/tree.ts:142-190` This is a pure type change — no runtime behavior changes. **Step 1: Add `ForkInfo` interface and fork fields** In `frontend/src/types/tree.ts`, after line 141 (the `ProceduralTreeStructure` closing brace), add `ForkInfo` then update `Tree` and `TreeListItem`: ```typescript export interface ForkInfo { parent_tree_id: string parent_tree_name: string | null fork_depth: number fork_reason: string | null has_parent_updates: boolean } ``` Add to `Tree` interface (after `usage_count: number`): ```typescript fork_info?: ForkInfo | null parent_tree_id?: string | null fork_depth?: number ``` Add to `TreeListItem` interface (after `visibility` field): ```typescript fork_depth?: number parent_tree_id?: string | null ``` **Step 2: Verify TypeScript compiles cleanly** ```bash cd /home/michaelchihlas/dev/patherly/frontend && npm run build 2>&1 | tail -20 ``` Expected: Clean build, no errors. **Step 3: Commit** ```bash cd /home/michaelchihlas/dev/patherly git add frontend/src/types/tree.ts git commit -m "feat: add ForkInfo type and fork fields to Tree/TreeListItem Co-Authored-By: Claude Sonnet 4.6 " ``` --- ### Task 2: Create `ForkModal` component **Files:** - Create: `frontend/src/components/library/ForkModal.tsx` **Step 1: Create the component file** Create `frontend/src/components/library/ForkModal.tsx` with this exact content: ```tsx import { useState } from 'react' import { GitBranch, X } from 'lucide-react' import { treesApi } from '@/api/trees' import { toast } from '@/lib/toast' import { cn } from '@/lib/utils' import { useNavigate } from 'react-router-dom' interface ForkModalProps { treeId: string treeName: string onClose: () => void } export function ForkModal({ treeId, treeName, onClose }: ForkModalProps) { const navigate = useNavigate() const [name, setName] = useState(`Copy of ${treeName}`) const [forkReason, setForkReason] = useState('') const [isSubmitting, setIsSubmitting] = useState(false) const [error, setError] = useState(null) const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() if (!name.trim()) return setIsSubmitting(true) setError(null) try { await treesApi.fork(treeId, { name: name.trim(), fork_reason: forkReason.trim() || undefined, }) toast.success('Flow forked successfully') onClose() navigate('/my-trees') } catch (err) { console.error('Failed to fork flow:', err) setError('Failed to fork flow. Please try again.') } finally { setIsSubmitting(false) } } return (
{/* Header */}

Fork Flow

{/* Body */}
setName(e.target.value)} required autoFocus className={cn( 'w-full rounded-lg border border-border bg-card px-3 py-2 text-sm text-foreground', 'placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20' )} />