@@ -30,7 +30,7 @@ import FloatingEdge from "./FloatingEdge";
3030import { EditorToolbar } from "./EditorToolbar" ;
3131import { parseRoadmapData } from "./helper" ;
3232import { LearningMap } from "./LearningMap" ;
33- import { Info , Redo , Undo , RotateCw , ShieldAlert } from "lucide-react" ;
33+ import { Info , Redo , Undo , RotateCw , ShieldAlert , X } from "lucide-react" ;
3434import useUndoable from "./useUndoable" ;
3535import { MultiNodePanel } from "./MultiNodePanel" ;
3636import { getTranslations } from "./translations" ;
@@ -82,6 +82,7 @@ export function LearningMapEditor({
8282 const [ settings , setSettings ] = useState < Settings > ( parsedRoadmap . settings ) ;
8383 const [ showGrid , setShowGrid ] = useState ( false ) ;
8484 const [ clipboard , setClipboard ] = useState < { nodes : Node < NodeData > [ ] ; edges : Edge [ ] } | null > ( null ) ;
85+ const [ lastMousePosition , setLastMousePosition ] = useState < { x : number ; y : number } | null > ( null ) ;
8586
8687 // Use language from settings if available, otherwise use prop
8788 const effectiveLanguage = settings ?. language || language ;
@@ -98,6 +99,7 @@ export function LearningMapEditor({
9899 { action : t . shortcuts . togglePreviewMode , shortcut : "Ctrl+P" } ,
99100 { action : t . shortcuts . toggleDebugMode , shortcut : "Ctrl+D" } ,
100101 { action : t . shortcuts . selectMultipleNodes , shortcut : "Ctrl+Click or Shift+Drag" } ,
102+ { action : t . shortcuts . selectAllNodes , shortcut : "Ctrl+A" } ,
101103 { action : t . shortcuts . showHelp , shortcut : "Ctrl+? or Help Button" } ,
102104 { action : t . shortcuts . zoomIn , shortcut : "Ctrl++" } ,
103105 { action : t . shortcuts . zoomOut , shortcut : "Ctrl+-" } ,
@@ -306,12 +308,16 @@ export function LearningMapEditor({
306308
307309 const addNewNode = useCallback (
308310 ( type : "task" | "topic" | "image" | "text" ) => {
309- const centerPos = screenToFlowPosition ( { x : window . innerWidth / 2 , y : window . innerHeight / 2 } ) ;
311+ // Use last mouse position if available, otherwise use center of screen
312+ const position = lastMousePosition
313+ ? screenToFlowPosition ( lastMousePosition )
314+ : screenToFlowPosition ( { x : window . innerWidth / 2 , y : window . innerHeight / 2 } ) ;
315+
310316 if ( type === "task" ) {
311317 const newNode : Node < NodeData > = {
312318 id : `node${ nextNodeId } ` ,
313319 type,
314- position : centerPos ,
320+ position,
315321 data : {
316322 label : t . newTask ,
317323 summary : "" ,
@@ -324,7 +330,7 @@ export function LearningMapEditor({
324330 const newNode : Node < NodeData > = {
325331 id : `node${ nextNodeId } ` ,
326332 type,
327- position : centerPos ,
333+ position,
328334 data : {
329335 label : t . newTopic ,
330336 summary : "" ,
@@ -339,7 +345,7 @@ export function LearningMapEditor({
339345 id : `background-node${ nextNodeId } ` ,
340346 type,
341347 zIndex : - 2 ,
342- position : centerPos ,
348+ position,
343349 data : {
344350 src : "" ,
345351 } ,
@@ -350,7 +356,7 @@ export function LearningMapEditor({
350356 const newNode : Node < TextNodeData > = {
351357 id : `background-node${ nextNodeId } ` ,
352358 type,
353- position : centerPos ,
359+ position,
354360 zIndex : - 1 ,
355361 data : {
356362 text : t . backgroundTextDefault ,
@@ -363,7 +369,7 @@ export function LearningMapEditor({
363369 }
364370 setSaved ( false ) ;
365371 } ,
366- [ nextNodeId , screenToFlowPosition , setNodes , setSaved , t ]
372+ [ nextNodeId , lastMousePosition , screenToFlowPosition , setNodes , setSaved , t ]
367373 ) ;
368374
369375 const handleSave = useCallback ( ( ) => {
@@ -679,13 +685,31 @@ export function LearningMapEditor({
679685 }
680686 } , [ setNodes , setEdges , setNextNodeId , setSaved , t ] ) ;
681687
688+ const handleSelectAll = useCallback ( ( ) => {
689+ setNodes ( nds => nds . map ( n => ( {
690+ ...n ,
691+ selected : true ,
692+ } ) ) )
693+ } , [ nodes , setSelectedNodeIds ] ) ;
694+
682695 const handleSelectionChange : OnSelectionChangeFunc = useCallback (
683696 ( { nodes : selectedNodes } ) => {
684697 setSelectedNodeIds ( selectedNodes . map ( n => n . id ) ) ;
685698 } ,
686699 [ setSelectedNodeIds ]
687700 ) ;
688701
702+ // Track mouse position for node placement
703+ useEffect ( ( ) => {
704+ const handleMouseMove = ( e : MouseEvent ) => {
705+ setLastMousePosition ( { x : e . clientX , y : e . clientY } ) ;
706+ } ;
707+ window . addEventListener ( "mousemove" , handleMouseMove ) ;
708+ return ( ) => {
709+ window . removeEventListener ( "mousemove" , handleMouseMove ) ;
710+ } ;
711+ } , [ ] ) ;
712+
689713 useEffect ( ( ) => {
690714 const handleKeyDown = ( e : KeyboardEvent ) => {
691715 //save shortcut
@@ -764,7 +788,6 @@ export function LearningMapEditor({
764788 handleZoomToSelection ( ) ;
765789 }
766790
767- console . log ( e ) ;
768791 // Toggle grid shortcut
769792 if ( ( e . ctrlKey || e . metaKey ) && e . code === "Backslash" ) {
770793 e . preventDefault ( ) ;
@@ -790,6 +813,11 @@ export function LearningMapEditor({
790813 e . preventDefault ( ) ;
791814 handlePaste ( ) ;
792815 }
816+ // Select all shortcut
817+ if ( ( e . ctrlKey || e . metaKey ) && e . key . toLowerCase ( ) === 'a' && ! e . shiftKey ) {
818+ e . preventDefault ( ) ;
819+ handleSelectAll ( ) ;
820+ }
793821
794822 // Dismiss with Escape
795823 if ( helpOpen && e . key === 'Escape' ) {
@@ -802,7 +830,7 @@ export function LearningMapEditor({
802830 } ;
803831 } , [ handleSave , handleUndo , handleRedo , addNewNode , helpOpen , setHelpOpen , togglePreviewMode , toggleDebugMode ,
804832 handleZoomIn , handleZoomOut , handleResetZoom , handleFitView , handleZoomToSelection , handleToggleGrid ,
805- handleResetMap , handleCut , handleCopy , handlePaste ] ) ;
833+ handleResetMap , handleCut , handleCopy , handlePaste , handleSelectAll ] ) ;
806834
807835 return (
808836 < >
@@ -822,6 +850,7 @@ export function LearningMapEditor({
822850 onDownlad = { handleDownload }
823851 onOpen = { handleOpen }
824852 onShare = { handleShare }
853+ onReset = { handleResetMap }
825854 language = { effectiveLanguage }
826855 />
827856 { previewMode && < LearningMap roadmapData = { roadmapState } language = { effectiveLanguage } /> }
@@ -907,24 +936,33 @@ export function LearningMapEditor({
907936 open = { helpOpen }
908937 onClose = { ( ) => setHelpOpen ( false ) }
909938 >
910- < h2 > { t . keyboardShortcuts } </ h2 >
911- < table >
912- < thead >
913- < tr >
914- < th > { t . action } </ th >
915- < th > { t . shortcut } </ th >
916- </ tr >
917- </ thead >
918- < tbody >
919- { keyboardShortcuts . map ( ( item ) => (
920- < tr key = { item . action } >
921- < td > { item . action } </ td >
922- < td > { item . shortcut } </ td >
939+ < header className = "help-header" >
940+ < h2 > { t . keyboardShortcuts } </ h2 >
941+ < button className = "close-button" onClick = { ( ) => setHelpOpen ( false ) } aria-label = { t . close } >
942+ < X size = { 20 } />
943+ </ button >
944+ </ header >
945+ < div className = "help-content" >
946+ < table >
947+ < thead >
948+ < tr >
949+ < th > { t . action } </ th >
950+ < th > { t . shortcut } </ th >
923951 </ tr >
924- ) ) }
925- </ tbody >
926- </ table >
927- < button className = "primary-button" onClick = { ( ) => setHelpOpen ( false ) } > { t . close } </ button >
952+ </ thead >
953+ < tbody >
954+ { keyboardShortcuts . map ( ( item ) => (
955+ < tr key = { item . action } >
956+ < td > { item . action } </ td >
957+ < td > { item . shortcut } </ td >
958+ </ tr >
959+ ) ) }
960+ </ tbody >
961+ </ table >
962+ </ div >
963+ < div className = "help-footer" >
964+ < button className = "primary-button" onClick = { ( ) => setHelpOpen ( false ) } > { t . close } </ button >
965+ </ div >
928966 </ dialog >
929967 < ShareDialog
930968 open = { shareDialogOpen }
0 commit comments