412 lines
25 KiB
Plaintext
412 lines
25 KiB
Plaintext
## Codebase Patterns
|
||
- Project uses Next.js 16 with App Router, TypeScript, and TailwindCSS 4
|
||
- Source files are in `src/` directory (app, components, lib, types)
|
||
- Supabase is configured with @supabase/supabase-js and @supabase/ssr packages
|
||
- Environment variables follow NEXT_PUBLIC_* convention for client-side access
|
||
- Use `npm run typecheck` to run TypeScript type checking (tsc --noEmit)
|
||
- Flowchart types exported from `src/types/flowchart.ts`
|
||
- Supabase migrations go in `supabase/migrations/` with timestamp prefix (YYYYMMDDHHMMSS_*.sql)
|
||
- Database has profiles table (linked to auth.users) and projects table (with flowchart_data JSONB)
|
||
- RLS policies enforce user_id = auth.uid() for project access
|
||
- Supabase client utilities in `src/lib/supabase/`: client.ts (browser), server.ts (App Router), middleware.ts (route protection)
|
||
- Next.js middleware.ts at project root handles route protection using updateSession helper
|
||
- Public auth routes: /login, /signup, /forgot-password, /reset-password
|
||
- Protected routes: /dashboard, /editor/* (redirect to /login if unauthenticated)
|
||
- Auth pages use 'use client' with useState, createClient() from lib/supabase/client.ts, and useRouter for redirects
|
||
- For lists with client-side updates (delete/add), use wrapper client component that receives initialData from server component
|
||
- Toast component in `src/components/Toast.tsx` for success/error notifications (auto-dismiss after 3s)
|
||
- Admin operations use SUPABASE_SERVICE_ROLE_KEY (server-side only via server actions)
|
||
- Admin users have is_admin=true in profiles table; check via .select('is_admin').eq('id', user.id).single()
|
||
- React Flow editor is in `src/app/editor/[projectId]/` with page.tsx (server) and FlowchartEditor.tsx (client)
|
||
- React Flow requires 'use client' and importing 'reactflow/dist/style.css'
|
||
- Use toReactFlowNodes/toReactFlowEdges helpers to convert app types to React Flow types
|
||
- Custom node components go in `src/components/editor/nodes/` with NodeProps<T> typing and useReactFlow() for updates
|
||
- Register custom node types in nodeTypes object (memoized with useMemo) and pass to ReactFlow component
|
||
- FlowchartEditor uses ReactFlowProvider wrapper + inner component pattern for useReactFlow() hook access
|
||
- Use nanoid for generating unique node IDs (import from 'nanoid')
|
||
|
||
---
|
||
|
||
## 2026-01-21 - US-001
|
||
- What was implemented: Project scaffolding and configuration
|
||
- Files changed:
|
||
- package.json - project dependencies and scripts
|
||
- tsconfig.json - TypeScript configuration
|
||
- next.config.ts - Next.js configuration
|
||
- postcss.config.mjs - PostCSS with TailwindCSS
|
||
- eslint.config.mjs - ESLint configuration
|
||
- .env.example - environment variables template
|
||
- .gitignore - git ignore rules
|
||
- src/app/ - Next.js App Router pages
|
||
- src/components/.gitkeep - components directory placeholder
|
||
- src/lib/.gitkeep - lib directory placeholder
|
||
- src/types/.gitkeep - types directory placeholder
|
||
- **Learnings for future iterations:**
|
||
- Next.js 16 uses `@tailwindcss/postcss` for TailwindCSS 4 integration
|
||
- Use --src-dir flag for create-next-app to put source in src/ folder
|
||
- npm package names can't have capital letters (use lowercase)
|
||
- .gitignore needs explicit exclusion for .env files, but include .env.example
|
||
---
|
||
|
||
## 2026-01-21 - US-002
|
||
- What was implemented: TypeScript types for flowchart data structures
|
||
- Files changed:
|
||
- src/types/flowchart.ts - new file with all flowchart type definitions
|
||
- package.json - added typecheck script (tsc --noEmit)
|
||
- **Learnings for future iterations:**
|
||
- Position is a helper type for {x, y} coordinates used by nodes
|
||
- FlowchartNode is a union type of DialogueNode | ChoiceNode | VariableNode
|
||
- ChoiceOption is a separate type to make options array cleaner
|
||
- All types use `export type` for TypeScript isolatedModules compatibility
|
||
---
|
||
|
||
## 2026-01-21 - US-003
|
||
- What was implemented: Supabase schema for users and projects
|
||
- Files changed:
|
||
- supabase/migrations/20260121000000_create_profiles_and_projects.sql - new file with all database schema
|
||
- **Learnings for future iterations:**
|
||
- Supabase migrations are plain SQL files in supabase/migrations/ directory
|
||
- Migration filenames use timestamp prefix (YYYYMMDDHHMMSS_description.sql)
|
||
- RLS policies need separate policies for SELECT, INSERT, UPDATE, DELETE operations
|
||
- Admin check policy uses EXISTS subquery to check is_admin flag on profiles table
|
||
- projects table references profiles.id (not auth.users.id directly) for proper FK relationships
|
||
- flowchart_data column uses JSONB type with default empty structure
|
||
- Added auto-update trigger for updated_at timestamp on projects table
|
||
---
|
||
|
||
## 2026-01-21 - US-004
|
||
- What was implemented: Supabase client configuration utilities
|
||
- Files changed:
|
||
- src/lib/supabase/client.ts - browser client using createBrowserClient from @supabase/ssr
|
||
- src/lib/supabase/server.ts - server client for App Router with async cookies() API
|
||
- src/lib/supabase/middleware.ts - middleware helper with updateSession function
|
||
- src/lib/.gitkeep - removed (no longer needed)
|
||
- **Learnings for future iterations:**
|
||
- @supabase/ssr package provides createBrowserClient and createServerClient functions
|
||
- Server client requires async cookies() from next/headers in Next.js 16
|
||
- Middleware client returns both user object and supabaseResponse for route protection
|
||
- Cookie handling uses getAll/setAll pattern for proper session management
|
||
- setAll in server.ts wrapped in try/catch to handle Server Component limitations
|
||
---
|
||
|
||
## 2026-01-21 - US-005
|
||
- What was implemented: Protected routes middleware for authentication
|
||
- Files changed:
|
||
- middleware.ts - new file at project root for route protection
|
||
- **Learnings for future iterations:**
|
||
- Next.js middleware.ts must be at project root (not in src/)
|
||
- updateSession helper from lib/supabase/middleware.ts returns { user, supabaseResponse }
|
||
- Use startsWith() for route matching to handle nested routes (e.g., /editor/*)
|
||
- Matcher config excludes static files and images to avoid unnecessary middleware calls
|
||
- Clone nextUrl before modifying pathname for redirects
|
||
---
|
||
|
||
## 2026-01-21 - US-006
|
||
- What was implemented: Login page with email/password authentication
|
||
- Files changed:
|
||
- src/app/login/page.tsx - new file with login form and Supabase auth
|
||
- **Learnings for future iterations:**
|
||
- Auth pages use 'use client' directive since they need useState and form handling
|
||
- Use createClient() from lib/supabase/client.ts for browser-side auth operations
|
||
- supabase.auth.signInWithPassword returns { error } object for handling failures
|
||
- useRouter from next/navigation for programmatic redirects after auth
|
||
- Error state displayed in red alert box with dark mode support
|
||
- Loading state disables submit button and shows "Signing in..." text
|
||
- TailwindCSS dark mode uses dark: prefix (e.g., dark:bg-zinc-950)
|
||
---
|
||
|
||
## 2026-01-21 - US-007
|
||
- What was implemented: Sign up page for invite-only account setup
|
||
- Files changed:
|
||
- src/app/signup/page.tsx - new file with signup form and Supabase auth
|
||
- **Learnings for future iterations:**
|
||
- Supabase invite tokens come via URL hash fragment (window.location.hash)
|
||
- Parse hash with URLSearchParams after removing leading '#'
|
||
- Check for type=invite or type=signup to detect invite flow
|
||
- Use setSession() with access_token and refresh_token to establish session from invite link
|
||
- For invited users, update password with updateUser() then create profile with upsert()
|
||
- Use upsert() instead of insert() for profiles to handle edge cases
|
||
- Validate password confirmation before submission (passwords match check)
|
||
- display_name defaults to email prefix (split('@')[0])
|
||
---
|
||
|
||
## 2026-01-21 - US-008
|
||
- What was implemented: Logout functionality component
|
||
- Files changed:
|
||
- src/components/LogoutButton.tsx - new client component with signOut and redirect
|
||
- src/components/.gitkeep - removed (no longer needed)
|
||
- **Learnings for future iterations:**
|
||
- LogoutButton is a reusable component that will be used in the navbar (US-011)
|
||
- Component uses 'use client' directive for client-side auth operations
|
||
- Loading state prevents double-clicks during signOut
|
||
- Styled with neutral zinc colors to work as a secondary button in navbars
|
||
---
|
||
|
||
## 2026-01-21 - US-009
|
||
- What was implemented: Password reset - forgot password page
|
||
- Files changed:
|
||
- src/app/forgot-password/page.tsx - new file with forgot password form and email reset
|
||
- **Learnings for future iterations:**
|
||
- resetPasswordForEmail requires redirectTo option to specify where user lands after clicking reset link
|
||
- Use `window.location.origin` to get the current site URL for redirectTo
|
||
- Page shows different UI after success (conditional rendering with success state)
|
||
- Use ' for apostrophe in JSX to avoid HTML entity issues
|
||
- Follow same styling pattern as login page for consistency across auth pages
|
||
---
|
||
|
||
## 2026-01-21 - US-010
|
||
- What was implemented: Password reset - set new password page
|
||
- Files changed:
|
||
- src/app/reset-password/page.tsx - new file with password reset form
|
||
- src/app/login/page.tsx - updated to show success message from password reset
|
||
- **Learnings for future iterations:**
|
||
- Supabase recovery tokens come via URL hash fragment with type=recovery
|
||
- Use setSession() with access_token and refresh_token from hash to establish recovery session
|
||
- Show loading state while verifying token validity (tokenValid === null)
|
||
- Show error state with link to request new reset if token is invalid
|
||
- After password update, sign out the user and redirect to login with success message
|
||
- Use query param (message=password_reset_success) to pass success state between pages
|
||
- Login page uses useSearchParams to read and display success messages
|
||
- Success messages styled with green background (bg-green-50)
|
||
---
|
||
|
||
## 2026-01-21 - US-011
|
||
- What was implemented: Dashboard layout with navbar component
|
||
- Files changed:
|
||
- src/app/dashboard/layout.tsx - new file with dashboard layout wrapper
|
||
- src/components/Navbar.tsx - new reusable navbar component
|
||
- **Learnings for future iterations:**
|
||
- Dashboard layout is a server component that fetches user data via createClient() from lib/supabase/server.ts
|
||
- Navbar accepts userEmail prop to display current user
|
||
- Layout wraps children with consistent max-w-7xl container and padding
|
||
- Navbar uses Link component to allow clicking app title to go back to dashboard
|
||
- Navbar has border-b styling with dark mode support for visual separation
|
||
- Use gap-4 for spacing between navbar items (user email and logout button)
|
||
---
|
||
|
||
## 2026-01-21 - US-012
|
||
- What was implemented: Dashboard page listing user projects
|
||
- Files changed:
|
||
- src/app/dashboard/page.tsx - new file with project listing, cards, and empty state
|
||
- **Learnings for future iterations:**
|
||
- Dashboard page is a server component that fetches projects directly from Supabase
|
||
- Use .eq('user_id', user.id) for RLS-backed queries (though RLS also enforces this)
|
||
- Order by updated_at descending to show most recent projects first
|
||
- formatDate() helper with toLocaleDateString for human-readable dates
|
||
- Project cards use Link component for navigation to /editor/[projectId]
|
||
- Empty state uses dashed border (border-dashed) with centered content and icon
|
||
- Hover effects on cards: border-blue-300, shadow-md, and text color change on title
|
||
- Error state displayed if Supabase query fails
|
||
---
|
||
|
||
## 2026-01-21 - US-013
|
||
- What was implemented: Create new project functionality
|
||
- Files changed:
|
||
- src/components/NewProjectButton.tsx - new client component with modal dialog
|
||
- src/app/dashboard/page.tsx - added NewProjectButton to header area
|
||
- src/app/signup/page.tsx - fixed lint error (setState in effect) by initializing email from searchParams
|
||
- **Learnings for future iterations:**
|
||
- Modal dialogs use fixed positioning with backdrop (bg-black/50) for overlay effect
|
||
- Form submission uses Supabase insert with .select('id').single() to get the new record ID
|
||
- Initialize flowchart_data with { nodes: [], edges: [] } for new projects
|
||
- router.push() for programmatic navigation after successful creation
|
||
- autoFocus on input for better UX when modal opens
|
||
- Prevent modal close while loading (check isLoading before calling handleClose)
|
||
- ESLint rule react-hooks/set-state-in-effect warns against synchronous setState in useEffect
|
||
- Initialize state from searchParams directly in useState() instead of setting in useEffect
|
||
---
|
||
|
||
## 2026-01-21 - US-014
|
||
- What was implemented: Delete project functionality with confirmation dialog and toast
|
||
- Files changed:
|
||
- src/components/ProjectCard.tsx - new client component replacing Link, with delete button and confirmation dialog
|
||
- src/components/ProjectList.tsx - new wrapper component to manage project list state and toast notifications
|
||
- src/components/Toast.tsx - new reusable toast notification component
|
||
- src/app/dashboard/page.tsx - updated to use ProjectList instead of inline rendering
|
||
- **Learnings for future iterations:**
|
||
- To enable client-side state updates (like removing items), extract list rendering from server components into client components
|
||
- ProjectList accepts initialProjects from server and manages state locally for immediate UI updates
|
||
- Use onDelete callback pattern to propagate deletion events from child (ProjectCard) to parent (ProjectList)
|
||
- Delete button uses e.stopPropagation() to prevent card click navigation when clicking delete
|
||
- Confirmation dialogs should disable close/cancel while action is in progress (isDeleting check)
|
||
- Toast component uses useCallback for handlers and auto-dismiss with setTimeout
|
||
- Toast animations can use TailwindCSS animate-in utilities (fade-in, slide-in-from-bottom-4)
|
||
- Delete icon appears on hover using group-hover:opacity-100 with parent group class
|
||
---
|
||
|
||
## 2026-01-21 - US-015
|
||
- What was implemented: Rename project functionality
|
||
- Files changed:
|
||
- src/components/ProjectCard.tsx - added rename button, modal dialog, and Supabase update logic
|
||
- src/components/ProjectList.tsx - added handleRename callback and toast notification
|
||
- **Learnings for future iterations:**
|
||
- Multiple action buttons on a card can be grouped in a flex container with gap-1
|
||
- Rename modal follows same pattern as delete dialog: fixed positioning, backdrop, form
|
||
- Use onKeyDown to handle Enter key for quick form submission
|
||
- Reset form state (newName, error) when opening modal to handle edge cases
|
||
- Check if name is unchanged before making API call to avoid unnecessary requests
|
||
- Trim whitespace from input value before validation and submission
|
||
- handleRename callback updates project name in state using map() to preserve list order
|
||
---
|
||
|
||
## 2026-01-21 - US-016
|
||
- What was implemented: Admin invite user functionality
|
||
- Files changed:
|
||
- src/app/admin/invite/page.tsx - new admin-only page with access check (redirects non-admins)
|
||
- src/app/admin/invite/InviteForm.tsx - client component with invite form and state management
|
||
- src/app/admin/invite/actions.ts - server action using service role key to call inviteUserByEmail
|
||
- src/components/Navbar.tsx - added isAdmin prop and "Invite User" link (visible only to admins)
|
||
- src/app/dashboard/layout.tsx - fetches profile.is_admin and passes it to Navbar
|
||
- .env.example - added SUPABASE_SERVICE_ROLE_KEY and NEXT_PUBLIC_SITE_URL
|
||
- **Learnings for future iterations:**
|
||
- Admin operations require SUPABASE_SERVICE_ROLE_KEY (server-side only, not NEXT_PUBLIC_*)
|
||
- Use createClient from @supabase/supabase-js directly for admin client (not @supabase/ssr)
|
||
- Admin client needs auth config: { autoRefreshToken: false, persistSession: false }
|
||
- inviteUserByEmail requires redirectTo option for the signup link in email
|
||
- Server actions ('use server') can access private env vars safely
|
||
- Admin check should happen both in server component (redirect) and server action (double check)
|
||
- Admin page uses its own layout (not dashboard layout) to have custom styling
|
||
---
|
||
|
||
## 2026-01-21 - US-017
|
||
- What was implemented: Editor page with React Flow canvas
|
||
- Files changed:
|
||
- package.json - added reactflow dependency
|
||
- src/app/editor/[projectId]/page.tsx - new server component that fetches project from Supabase, handles auth/not found, renders header with back link
|
||
- src/app/editor/[projectId]/FlowchartEditor.tsx - new client component with React Flow canvas, Background component, type converters for nodes/edges
|
||
- src/app/editor/[projectId]/loading.tsx - new loading state component with spinner
|
||
- **Learnings for future iterations:**
|
||
- React Flow requires 'use client' directive since it uses browser APIs
|
||
- Import 'reactflow/dist/style.css' for default React Flow styling
|
||
- Use useNodesState and useEdgesState hooks for managing nodes/edges state
|
||
- Convert app types (FlowchartNode, FlowchartEdge) to React Flow types with helper functions
|
||
- Next.js dynamic route params come as Promise in App Router 16+ (need to await params)
|
||
- Use notFound() from next/navigation for 404 responses
|
||
- React Flow canvas needs parent container with explicit height (h-full, h-screen)
|
||
- Background component accepts variant (Dots, Lines, Cross) and gap/size props
|
||
- Loading page (loading.tsx) provides automatic loading UI for async server components
|
||
---
|
||
|
||
## 2026-01-21 - US-018
|
||
- What was implemented: Canvas pan and zoom controls
|
||
- Files changed:
|
||
- src/app/editor/[projectId]/FlowchartEditor.tsx - added Controls import and component
|
||
- **Learnings for future iterations:**
|
||
- React Flow Controls component provides zoom +/-, fitView, and lock buttons out of the box
|
||
- Use position="bottom-right" prop to position controls in bottom-right corner
|
||
- Pan (click-and-drag) and zoom (mouse wheel) are React Flow defaults, no extra config needed
|
||
---
|
||
|
||
## 2026-01-21 - US-019
|
||
- What was implemented: Editor toolbar with add/save/export/import buttons
|
||
- Files changed:
|
||
- src/components/editor/Toolbar.tsx - new toolbar component with styled buttons
|
||
- src/app/editor/[projectId]/FlowchartEditor.tsx - integrated toolbar with placeholder handlers
|
||
- **Learnings for future iterations:**
|
||
- Toolbar component accepts callback props for actions (onAddDialogue, onSave, etc.)
|
||
- Node type buttons use color coding: blue (Dialogue), green (Choice), orange (Variable)
|
||
- Action buttons (Save, Export, Import) use neutral bordered styling
|
||
- FlowchartEditor now uses flex-col layout to stack toolbar above canvas
|
||
- Placeholder handlers with TODO comments help track future implementation work
|
||
---
|
||
|
||
## 2026-01-21 - US-020
|
||
- What was implemented: Custom DialogueNode component for displaying/editing character dialogue
|
||
- Files changed:
|
||
- src/components/editor/nodes/DialogueNode.tsx - new custom node component with editable speaker and text fields
|
||
- src/app/editor/[projectId]/FlowchartEditor.tsx - registered DialogueNode as custom node type
|
||
- **Learnings for future iterations:**
|
||
- Custom React Flow nodes use NodeProps<T> for typing, where T is the data shape
|
||
- Use useReactFlow() hook to get setNodes for updating node data from within the node component
|
||
- Handle components need Position enum (Position.Top, Position.Bottom) for positioning
|
||
- Custom handles can be styled with className and TailwindCSS, use ! prefix to override defaults (e.g., !h-3, !w-3)
|
||
- Node types must be registered in a nodeTypes object and passed to ReactFlow component
|
||
- Memoize nodeTypes with useMemo to prevent unnecessary re-renders
|
||
- Custom node components go in src/components/editor/nodes/ directory
|
||
---
|
||
|
||
## 2026-01-21 - US-021
|
||
- What was implemented: Add dialogue node from toolbar functionality
|
||
- Files changed:
|
||
- package.json - added nanoid dependency for unique ID generation
|
||
- src/app/editor/[projectId]/FlowchartEditor.tsx - implemented handleAddDialogue to create new dialogue nodes at viewport center
|
||
- **Learnings for future iterations:**
|
||
- useReactFlow() hook requires ReactFlowProvider wrapper, so split component into inner component and outer wrapper
|
||
- getViewport() returns { x, y, zoom } representing the current pan/zoom state
|
||
- Calculate viewport center: centerX = (-viewport.x + halfWidth) / viewport.zoom
|
||
- nanoid v5+ generates unique IDs synchronously with no dependencies
|
||
- Node creation pattern: create Node object with { id, type, position, data }, then add to state via setNodes
|
||
- React Flow nodes are draggable by default, no extra configuration needed
|
||
---
|
||
|
||
## 2026-01-21 - US-022
|
||
- What was implemented: Custom ChoiceNode component for displaying branching decisions
|
||
- Files changed:
|
||
- src/components/editor/nodes/ChoiceNode.tsx - new custom node component with green styling, editable prompt, and dynamic option handles
|
||
- src/app/editor/[projectId]/FlowchartEditor.tsx - registered ChoiceNode as custom node type
|
||
- **Learnings for future iterations:**
|
||
- ChoiceNode follows same pattern as DialogueNode: NodeProps<T> typing, useReactFlow() for updates
|
||
- Dynamic handles positioned using style={{ left: `${((index + 1) / (options.length + 1)) * 100}%` }} for even spacing
|
||
- Handle id format for options: 'option-0', 'option-1', etc. (matching the index)
|
||
- Each option needs a unique id (string) and label (string) per the ChoiceOption type
|
||
- updateOptionLabel callback pattern: find option by id, map over options array to update matching one
|
||
---
|
||
|
||
## 2026-01-21 - US-023
|
||
- What was implemented: Add choice node from toolbar functionality
|
||
- Files changed:
|
||
- src/app/editor/[projectId]/FlowchartEditor.tsx - implemented handleAddChoice to create new choice nodes at viewport center
|
||
- **Learnings for future iterations:**
|
||
- handleAddChoice follows same pattern as handleAddDialogue: get viewport center, create node with nanoid, add to state
|
||
- Choice nodes must be initialized with 2 options (each with unique id via nanoid and empty label)
|
||
- Node data structure for choice: { prompt: '', options: [{ id, label }, { id, label }] }
|
||
- React Flow nodes are draggable by default after being added to state
|
||
---
|
||
|
||
## 2026-01-21 - US-024
|
||
- What was implemented: Add/remove choice options functionality (2-6 options supported)
|
||
- Files changed:
|
||
- src/components/editor/nodes/ChoiceNode.tsx - added addOption and removeOption callbacks, '+' button to add options, 'x' button per option to remove
|
||
- **Learnings for future iterations:**
|
||
- Define MIN_OPTIONS and MAX_OPTIONS constants for clear limits
|
||
- Use disabled prop on buttons to enforce min/max constraints with appropriate styling (opacity-30, cursor-not-allowed)
|
||
- Remove button uses × character for simple cross icon
|
||
- Add button styled with border-dashed for visual distinction from action buttons
|
||
- Handles update dynamically via React Flow re-render when options array changes
|
||
---
|
||
|
||
## 2026-01-21 - US-025
|
||
- What was implemented: Custom VariableNode component for setting/modifying story variables
|
||
- Files changed:
|
||
- src/components/editor/nodes/VariableNode.tsx - new custom node component with orange styling, editable variable name, operation dropdown, and numeric value input
|
||
- src/app/editor/[projectId]/FlowchartEditor.tsx - imported and registered VariableNode in nodeTypes
|
||
- **Learnings for future iterations:**
|
||
- VariableNode follows same pattern as DialogueNode: NodeProps<T> typing, useReactFlow() for updates
|
||
- Use parseFloat() with fallback to 0 for number input handling: `parseFloat(e.target.value) || 0`
|
||
- Operation dropdown uses select element with options for 'set', 'add', 'subtract'
|
||
- Type assertion needed for select value: `e.target.value as 'set' | 'add' | 'subtract'`
|
||
- Use `??` (nullish coalescing) for number defaults instead of `||` to allow 0 values: `data.value ?? 0`
|
||
---
|
||
|
||
## 2026-01-21 - US-026
|
||
- What was implemented: Add variable node from toolbar functionality
|
||
- Files changed:
|
||
- src/app/editor/[projectId]/FlowchartEditor.tsx - implemented handleAddVariable to create new variable nodes at viewport center
|
||
- **Learnings for future iterations:**
|
||
- handleAddVariable follows same pattern as handleAddDialogue and handleAddChoice: get viewport center, create node with nanoid, add to state
|
||
- Variable nodes initialized with { variableName: '', operation: 'set', value: 0 }
|
||
- All add node handlers share the same pattern and use the getViewportCenter helper
|
||
---
|
||
|
||
## 2026-01-21 - US-027
|
||
- What was implemented: Connect nodes with edges including arrow markers and smooth styling
|
||
- Files changed:
|
||
- src/app/editor/[projectId]/FlowchartEditor.tsx - added MarkerType import, updated onConnect to create edges with smoothstep type and arrow markers, updated toReactFlowEdges to apply same styling to loaded edges
|
||
- **Learnings for future iterations:**
|
||
- Use `type: 'smoothstep'` for cleaner edge curves instead of default bezier
|
||
- Use `markerEnd: { type: MarkerType.ArrowClosed }` to add directional arrows to edges
|
||
- Connection type has nullable source/target, but Edge requires non-null strings - guard with early return
|
||
- Apply consistent edge styling in both onConnect (new edges) and toReactFlowEdges (loaded edges)
|
||
- Generate unique edge IDs with nanoid in onConnect callback
|
||
---
|