1+ use crate :: platform:: pick_folder;
12use dioxus:: prelude:: * ;
3+ use markdown_neuraxis_config:: Config ;
24use markdown_neuraxis_engine:: {
35 Document , FileTree , MarkdownFile , Snapshot , editing:: commands:: Cmd , io,
46} ;
@@ -38,20 +40,24 @@ pub fn App(notes_path: PathBuf) -> Element {
3840 notes_path. display( )
3941 ) ;
4042
43+ // Notes path as a signal so it can be changed at runtime
44+ let notes_path = use_signal ( || notes_path) ;
45+
4146 // Error state for runtime errors
4247 let mut error_state = use_signal ( || None :: < RuntimeError > ) ;
4348
4449 // Build file tree
4550 let mut file_tree = use_signal ( || {
46- log:: info!( "Building file tree for: {}" , notes_path. display( ) ) ;
47- match io:: build_file_tree ( & notes_path) {
51+ let path = notes_path. read ( ) ;
52+ log:: info!( "Building file tree for: {}" , path. display( ) ) ;
53+ match io:: build_file_tree ( & path) {
4854 Ok ( tree) => {
4955 log:: info!( "File tree built successfully" ) ;
5056 tree
5157 }
5258 Err ( e) => {
5359 log:: error!( "Error building file tree: {e}" ) ;
54- FileTree :: new ( notes_path . clone ( ) )
60+ FileTree :: new ( path . clone ( ) )
5561 }
5662 }
5763 } ) ;
@@ -66,17 +72,17 @@ pub fn App(notes_path: PathBuf) -> Element {
6672
6773 // Create callbacks outside the rsx! block for cleaner code
6874 let on_sidebar_file_select = {
69- let notes_path = notes_path. clone ( ) ;
7075 let mut selected_file = selected_file;
7176 let mut current_document = current_document;
7277 let mut current_snapshot = current_snapshot;
7378 let mut error_state = error_state;
7479 let mut mobile_nav_open = mobile_nav_open;
7580 let mut focused_folder = focused_folder;
7681 move |markdown_file : MarkdownFile | {
82+ let path = notes_path. read ( ) ;
7783 load_existing_document (
7884 & markdown_file,
79- & notes_path ,
85+ & path ,
8086 & mut selected_file,
8187 & mut current_document,
8288 & mut current_snapshot,
@@ -90,15 +96,15 @@ pub fn App(notes_path: PathBuf) -> Element {
9096 } ;
9197
9298 let on_file_navigate = {
93- let notes_path = notes_path. clone ( ) ;
9499 let mut selected_file = selected_file;
95100 let mut current_document = current_document;
96101 let mut current_snapshot = current_snapshot;
97102 let mut error_state = error_state;
98103 move |file_path : PathBuf | {
104+ let path = notes_path. read ( ) ;
99105 navigate_to_path (
100106 file_path,
101- & notes_path ,
107+ & path ,
102108 & mut selected_file,
103109 & mut current_document,
104110 & mut current_snapshot,
@@ -108,14 +114,14 @@ pub fn App(notes_path: PathBuf) -> Element {
108114 } ;
109115
110116 let on_wikilink_navigate = {
111- let notes_path = notes_path. clone ( ) ;
112117 let mut selected_file = selected_file;
113118 let mut current_document = current_document;
114119 let mut current_snapshot = current_snapshot;
115120 let mut error_state = error_state;
116121 let mut file_tree = file_tree;
117122 let mut focused_folder = focused_folder;
118123 move |target : String | {
124+ let path = notes_path. read ( ) ;
119125 // First check if target matches a folder
120126 let folder_path = file_tree. read ( ) . find_folder ( & target) ;
121127 if let Some ( folder_path) = folder_path {
@@ -131,7 +137,7 @@ pub fn App(notes_path: PathBuf) -> Element {
131137
132138 // Not a folder, resolve as file - clear any folder focus
133139 focused_folder. set ( None ) ;
134- let markdown_file = resolve_wikilink ( & target, & notes_path ) ;
140+ let markdown_file = resolve_wikilink ( & target, & path ) ;
135141 // Expand parent folders so the file is visible in the tree
136142 if let Some ( parent) = markdown_file. relative_path ( ) . parent ( )
137143 && !parent. as_str ( ) . is_empty ( )
@@ -142,7 +148,7 @@ pub fn App(notes_path: PathBuf) -> Element {
142148 }
143149 load_document (
144150 markdown_file,
145- & notes_path ,
151+ & path ,
146152 & mut selected_file,
147153 & mut current_document,
148154 & mut current_snapshot,
@@ -152,7 +158,7 @@ pub fn App(notes_path: PathBuf) -> Element {
152158 } ;
153159
154160 let on_command = create_command_callback (
155- notes_path. clone ( ) ,
161+ notes_path,
156162 selected_file,
157163 current_document,
158164 current_snapshot,
@@ -182,7 +188,67 @@ pub fn App(notes_path: PathBuf) -> Element {
182188 }
183189 div {
184190 class: if * mobile_nav_open. read( ) { "sidebar mobile-visible" } else { "sidebar" } ,
185- h2 { "Files" }
191+ div {
192+ class: "sidebar-header" ,
193+ h2 { "Files" }
194+ button {
195+ class: "change-folder-btn" ,
196+ title: "Change notes folder" ,
197+ onclick: move |_| {
198+ let mut notes_path = notes_path;
199+ let mut file_tree = file_tree;
200+ let mut selected_file = selected_file;
201+ let mut current_document = current_document;
202+ let mut current_snapshot = current_snapshot;
203+ let mut focused_folder = focused_folder;
204+ let mut error_state = error_state;
205+
206+ let current_path = notes_path. read( ) . clone( ) ;
207+ spawn( async move {
208+ if let Some ( new_path) = pick_folder( Some ( & current_path) ) . await {
209+ // Save the new path to config
210+ let config = Config { notes_path: new_path. clone( ) } ;
211+ match config. save( ) {
212+ Ok ( ( ) ) => {
213+ log:: info!( "Config saved with new notes path: {}" , new_path. display( ) ) ;
214+
215+ // Update notes_path signal
216+ notes_path. set( new_path. clone( ) ) ;
217+
218+ // Rebuild file tree
219+ match io:: build_file_tree( & new_path) {
220+ Ok ( tree) => {
221+ log:: info!( "File tree rebuilt successfully" ) ;
222+ file_tree. set( tree) ;
223+ }
224+ Err ( e) => {
225+ log:: error!( "Error building file tree: {e}" ) ;
226+ file_tree. set( FileTree :: new( new_path) ) ;
227+ }
228+ }
229+
230+ // Clear current file state
231+ selected_file. set( None ) ;
232+ current_document. set( None ) ;
233+ current_snapshot. set( None ) ;
234+ focused_folder. set( None ) ;
235+ error_state. set( None ) ;
236+ }
237+ Err ( e) => {
238+ RuntimeError :: log_and_set(
239+ & mut error_state,
240+ "Failed to save config" . to_string( ) ,
241+ e,
242+ ) ;
243+ }
244+ }
245+ }
246+ // If None (cancelled), do nothing
247+ } ) ;
248+ } ,
249+ "📂"
250+ }
251+ }
186252 super :: components:: TreeView {
187253 tree: ReadSignal :: from( file_tree) ,
188254 selected_file: selected_file. read( ) . clone( ) ,
@@ -203,7 +269,7 @@ pub fn App(notes_path: PathBuf) -> Element {
203269 super :: components:: MainPanel {
204270 file: file. clone( ) ,
205271 snapshot: snapshot. clone( ) ,
206- notes_path: notes_path. clone( ) ,
272+ notes_path: notes_path. read ( ) . clone( ) ,
207273 document: document. clone( ) ,
208274 on_file_select: Some ( Callback :: new( on_file_navigate) ) ,
209275 on_command,
@@ -368,14 +434,15 @@ pub fn resolve_wikilink(target: &str, _notes_path: &Path) -> MarkdownFile {
368434
369435/// Create a command callback for document editing
370436fn create_command_callback (
371- notes_path : PathBuf ,
437+ notes_path : Signal < PathBuf > ,
372438 selected_file : Signal < Option < MarkdownFile > > ,
373439 mut current_document : Signal < Option < Arc < Document > > > ,
374440 mut current_snapshot : Signal < Option < Snapshot > > ,
375441 mut file_tree : Signal < FileTree > ,
376442 mut error_state : Signal < Option < RuntimeError > > ,
377443) -> impl FnMut ( Cmd ) + ' static {
378444 move |cmd : Cmd | {
445+ let path = notes_path. read ( ) ;
379446 let document_arc = current_document. read ( ) . clone ( ) ;
380447 if let Some ( mut document_arc) = document_arc {
381448 // Use Arc::make_mut for efficient copy-on-write
@@ -388,13 +455,13 @@ fn create_command_callback(
388455 let content = document. text ( ) ;
389456
390457 // Check if file exists before writing
391- let file_existed = io:: read_file ( file. relative_path ( ) , & notes_path ) . is_ok ( ) ;
458+ let file_existed = io:: read_file ( file. relative_path ( ) , & path ) . is_ok ( ) ;
392459
393- match io:: write_file ( file. relative_path ( ) , & notes_path , & content) {
460+ match io:: write_file ( file. relative_path ( ) , & path , & content) {
394461 Ok ( ( ) ) => {
395462 if !file_existed {
396- let absolute_path = file. relative_path ( ) . to_path ( & notes_path ) ;
397- file_tree. write ( ) . add_file ( & absolute_path, & notes_path ) ;
463+ let absolute_path = file. relative_path ( ) . to_path ( & * path ) ;
464+ file_tree. write ( ) . add_file ( & absolute_path, & path ) ;
398465 log:: info!(
399466 "New file created and auto-saved: {:?}" ,
400467 file. relative_path( )
0 commit comments