import { useEffect } from 'react' import { useEditor, react } from 'tldraw' import { toolBrushStylesAtom } from '../utils/brushUtils' export function BrushManager() { const editor = useEditor() // Tools that should have completely independent styles const DRAWING_TOOLS = ['draw', 'highlight'] // 1. Capture tools' brushes ONLY when user actively changes them (not on tool switch) useEffect(() => { let previousStyles: Record = {} let previousTool = editor.getCurrentToolId() const unreact = react('capture-user-brush', () => { const selection = editor.getSelectedShapeIds() const toolId = editor.getCurrentToolId() const activeStyles = editor.getInstanceState().stylesForNextShape // Skip select tool if (toolId === 'select') return // Detect if tool changed const toolChanged = toolId !== previousTool previousTool = toolId // For drawing tools: ONLY capture if user changes style while already on the tool // Do NOT capture when switching TO the tool (prevents contamination) if (DRAWING_TOOLS.includes(toolId)) { if (toolChanged) { // Just switched to this tool - don't capture, just record current state previousStyles[toolId] = JSON.parse(JSON.stringify(activeStyles)) return } // Already on this tool - check if user changed something const prevForThisTool = previousStyles[toolId] if (prevForThisTool && JSON.stringify(prevForThisTool) !== JSON.stringify(activeStyles)) { // User actively changed the style! Save it. const currentStored = toolBrushStylesAtom.get() toolBrushStylesAtom.set({ ...currentStored, [toolId]: JSON.parse(JSON.stringify(activeStyles)) }) previousStyles[toolId] = JSON.parse(JSON.stringify(activeStyles)) } return } // For non-drawing tools: capture when no selection if (selection.length === 0) { const currentStored = toolBrushStylesAtom.get() if (JSON.stringify(currentStored[toolId]) !== JSON.stringify(activeStyles)) { toolBrushStylesAtom.set({ ...currentStored, [toolId]: JSON.parse(JSON.stringify(activeStyles)) }) } } }) return () => { unreact() } }, [editor]) // 2. Restore the saved brush for ALL tools (including drawing tools) useEffect(() => { const restoreBrush = () => { const toolId = editor.getCurrentToolId() // Skip select tool only if (toolId === 'select') return const storedStyles = toolBrushStylesAtom.get()[toolId] if (storedStyles && Object.keys(storedStyles).length > 0) { editor.run(() => { for (const [id, value] of Object.entries(storedStyles)) { // Use the internal style ID to set the next style editor.setStyleForNextShapes({ id } as any, value, { history: 'ignore' }) } }, { history: 'ignore' }) } } // Restore when: // - Tool changes // - Selection is cleared // - A pointer operation finishes (to overwrite tldraw's auto-sync-to-selection) const handleEvent = (e: any) => { if (e.name === 'tool_change') { restoreBrush() } if (e.name === 'pointer_up') { // Wait for tldraw to finish its own sync-to-selection setTimeout(restoreBrush, 20) } } editor.on('event', handleEvent) // Also watch for selection becoming empty via react const unreact = react('restore-on-deselect', () => { const selection = editor.getSelectedShapeIds() if (selection.length === 0) { restoreBrush() } }) return () => { editor.off('event', handleEvent) unreact() } }, [editor]) // 3. EXPLICIT PROTECTION: Prevent drawing tools from syncing styles from selections useEffect(() => { let lastToolId = editor.getCurrentToolId() return react('block-drawing-tool-sync', () => { const toolId = editor.getCurrentToolId() const selection = editor.getSelectedShapeIds() // Detect tool change if (toolId !== lastToolId) { lastToolId = toolId // If we switched TO a drawing tool AND there's a selection // Force restore the drawing tool's saved styles immediately if (DRAWING_TOOLS.includes(toolId) && selection.length > 0) { const storedStyles = toolBrushStylesAtom.get()[toolId] if (storedStyles && Object.keys(storedStyles).length > 0) { // Multiple aggressive restores to override tldraw's sync for (let i = 0; i < 3; i++) { setTimeout(() => { editor.run(() => { for (const [id, value] of Object.entries(storedStyles)) { editor.setStyleForNextShapes({ id } as any, value, { history: 'ignore' }) } }, { history: 'ignore' }) }, i * 10) } } } } }) }, [editor]) return null }