@@ -4,13 +4,14 @@ import {
44 PersistedFairyState ,
55} from '@tldraw/fairy-shared'
66import { useCallback , useEffect , useRef } from 'react'
7- import { react , throttle , useEditor , useValue } from 'tldraw'
7+ import { react , throttle , useEditor , useToasts , useValue } from 'tldraw'
88import { useApp } from '../tla/hooks/useAppState'
99import { useTldrawUser } from '../tla/hooks/useUser'
1010import { FairyAgent } from './fairy-agent/agent/FairyAgent'
11+ import { $fairyProjects } from './FairyProjects'
12+ import { FairyTaskDragTool } from './FairyTaskDragTool'
13+ import { $fairyTasks , $showCanvasFairyTasks } from './FairyTaskList'
1114import { FairyThrowTool } from './FairyThrowTool'
12- import { $sharedTodoList , $showCanvasTodos } from './SharedTodoList'
13- import { TodoDragTool } from './TodoDragTool'
1415
1516export function FairyApp ( {
1617 setAgents,
@@ -22,6 +23,7 @@ export function FairyApp({
2223 const editor = useEditor ( )
2324 const user = useTldrawUser ( )
2425 const app = useApp ( )
26+ const toasts = useToasts ( )
2527 const fairyConfigs = useValue (
2628 'fairyConfigs' ,
2729 ( ) => JSON . parse ( app ?. getUser ( ) . fairies || '{}' ) as PersistedFairyConfigs ,
@@ -33,9 +35,18 @@ export function FairyApp({
3335 return await user . getToken ( )
3436 } , [ user ] )
3537
36- const handleError = useCallback ( ( e : any ) => {
37- console . error ( 'Error:' , e )
38- } , [ ] )
38+ const handleError = useCallback (
39+ ( e : any ) => {
40+ const message = typeof e === 'string' ? e : e instanceof Error && e . message
41+ toasts . addToast ( {
42+ title : 'Error' ,
43+ description : message || 'An error occurred' ,
44+ severity : 'error' ,
45+ } )
46+ console . error ( e )
47+ } ,
48+ [ toasts ]
49+ )
3950
4051 // Track whether we're currently loading state to prevent premature saves
4152 const isLoadingStateRef = useRef ( false )
@@ -45,18 +56,20 @@ export function FairyApp({
4556 const loadedAgentIdsRef = useRef < Set < string > > ( new Set ( ) )
4657 const sharedTodoListLoadedRef = useRef ( false )
4758 const showCanvasTodosLoadedRef = useRef ( false )
59+ const projectsLoadedRef = useRef ( false )
4860
4961 // Create agents dynamically from configs
5062 useEffect ( ( ) => {
5163 if ( ! editor ) return
5264
5365 // Register the FairyThrowTool
54- editor . removeTool ( FairyThrowTool )
55- editor . setTool ( FairyThrowTool )
66+ const selectTool = editor . root . children ! . select
67+ editor . removeTool ( FairyThrowTool , selectTool )
68+ editor . setTool ( FairyThrowTool , selectTool )
5669
5770 // Register the TodoDragTool
58- editor . removeTool ( TodoDragTool )
59- editor . setTool ( TodoDragTool )
71+ editor . removeTool ( FairyTaskDragTool , selectTool )
72+ editor . setTool ( FairyTaskDragTool , selectTool )
6073
6174 const configIds = Object . keys ( fairyConfigs )
6275 const existingAgents = agentsRef . current
@@ -105,7 +118,7 @@ export function FairyApp({
105118
106119 // Load fairy state from backend when agents are created
107120 useEffect ( ( ) => {
108- if ( ! app || agentsRef . current . length === 0 || ! $sharedTodoList || ! $showCanvasTodos || ! fileId )
121+ if ( ! app || agentsRef . current . length === 0 || ! $fairyTasks || ! $showCanvasFairyTasks || ! fileId )
109122 return
110123
111124 const fileState = app . getFileState ( fileId )
@@ -130,16 +143,22 @@ export function FairyApp({
130143
131144 // Load shared todo list only once
132145 if ( fairyState . sharedTodoList && ! sharedTodoListLoadedRef . current ) {
133- $sharedTodoList . set ( fairyState . sharedTodoList )
146+ $fairyTasks . set ( fairyState . sharedTodoList )
134147 sharedTodoListLoadedRef . current = true
135148 }
136149
137150 // Load show canvas todos only once
138151 if ( fairyState . showCanvasTodos && ! showCanvasTodosLoadedRef . current ) {
139- $showCanvasTodos . set ( fairyState . showCanvasTodos )
152+ $showCanvasFairyTasks . set ( fairyState . showCanvasTodos )
140153 showCanvasTodosLoadedRef . current = true
141154 }
142155
156+ // Load projects only once
157+ if ( fairyState . projects && ! projectsLoadedRef . current ) {
158+ $fairyProjects . set ( fairyState . projects )
159+ projectsLoadedRef . current = true
160+ }
161+
143162 // Allow a tick for state to settle before allowing saves
144163 setTimeout ( ( ) => {
145164 isLoadingStateRef . current = false
@@ -154,8 +173,7 @@ export function FairyApp({
154173 // Todo: Use FileStateUpdater for this
155174 // Save fairy state to backend periodically
156175 useEffect ( ( ) => {
157- if ( ! app || agentsRef . current . length === 0 || ! $sharedTodoList || ! $showCanvasTodos || ! fileId )
158- return
176+ if ( ! app || agentsRef . current . length === 0 || ! fileId ) return
159177
160178 const updateFairyState = throttle ( ( ) => {
161179 // Don't save if we're currently loading state
@@ -169,8 +187,9 @@ export function FairyApp({
169187 } ,
170188 { } as Record < string , PersistedFairyAgentState >
171189 ) ,
172- sharedTodoList : $sharedTodoList . get ( ) ,
173- showCanvasTodos : $showCanvasTodos . get ( ) ,
190+ sharedTodoList : $fairyTasks . get ( ) ,
191+ showCanvasTodos : $showCanvasFairyTasks . get ( ) ,
192+ projects : $fairyProjects . get ( ) ,
174193 }
175194 app . onFairyStateUpdate ( fileId , fairyState )
176195 } , 2000 ) // Save maximum every 2 seconds
@@ -182,26 +201,21 @@ export function FairyApp({
182201 agent . $fairyEntity . get ( )
183202 agent . $chatHistory . get ( )
184203 agent . $todoList . get ( )
185- agent . $contextItems . get ( )
186204 updateFairyState ( )
187205 } )
188206 fairyCleanupFns . push ( cleanup )
189207 } )
190208
191- const cleanupSharedTodoList = react ( 'shared todo list' , ( ) => {
192- $sharedTodoList . get ( )
193- updateFairyState ( )
194- } )
195-
196- const cleanupShowCanvasTodos = react ( 'show canvas todos' , ( ) => {
197- $showCanvasTodos . get ( )
209+ const cleanupSharedFairyState = react ( 'shared fairy atom state' , ( ) => {
210+ $fairyTasks . get ( )
211+ $showCanvasFairyTasks . get ( )
212+ $fairyProjects . get ( )
198213 updateFairyState ( )
199214 } )
200215
201216 return ( ) => {
202217 updateFairyState . flush ( )
203- cleanupSharedTodoList ( )
204- cleanupShowCanvasTodos ( )
218+ cleanupSharedFairyState ( )
205219 fairyCleanupFns . forEach ( ( cleanup ) => cleanup ( ) )
206220 }
207221 } , [ app , fairyConfigs , fileId ] )
0 commit comments