diff --git a/crates/ui-egui/src/folders.rs b/crates/ui-egui/src/folders.rs index 9818db05..8e09d6e0 100644 --- a/crates/ui-egui/src/folders.rs +++ b/crates/ui-egui/src/folders.rs @@ -87,6 +87,11 @@ pub fn picked(app: &mut CraftApp, folder: PathBuf) { /// "Work on my project" picked a folder: its files are the app, edited in place (nothing asked). /// An existing project opens; an empty folder has nothing to work on. pub fn picked_own(app: &mut CraftApp, folder: PathBuf) { + // The dialog runs beside the window: a pick that ends after leaving the door is dropped, or + // a version made on Make would edit that folder. + if !app.state.make.mine { + return; + } match workspace::folder_state(&folder) { Ok(FolderState::Project) => open_project(app, &folder), Ok(FolderState::Empty) => app.toast(tr("That folder is empty. Pick the folder your project's files are in."), true), diff --git a/crates/ui-egui/src/make.rs b/crates/ui-egui/src/make.rs index 6f7fff2e..61e17894 100644 --- a/crates/ui-egui/src/make.rs +++ b/crates/ui-egui/src/make.rs @@ -44,7 +44,11 @@ pub fn normalize_url(s: &str) -> String { } /// The target the draft will use: the picked one, the typed address, or an address in the request. +/// "Work on my project" has none: an address in its request is a link to add, not an original. pub fn resolve_target(d: &MakeDraft) -> TargetRef { + if d.mine { + return TargetRef::None; + } if d.target != TargetRef::None { return d.target.clone(); } @@ -69,12 +73,17 @@ pub fn hero(app: &mut CraftApp, ui: &mut Ui, w: f32) { let d = &mut app.state.make; let door = if d.look { 2 } else { usize::from(d.mine) }; if let Some(i) = widgets::segmented_sized(ui, &["Make my own version", "Work on my project", "Just look inside"], door, 38.0, 12.5) { + let left = d.mine && i != 1; (d.mine, d.look) = (i == 1, i == 2); if d.mine { // The person's own project: there's nothing to recreate. (d.target, d.editing_url, d.found, d.builder, d.game) = (TargetRef::None, false, None, None, false); d.url.clear(); } + if left && d.existing == Some(ExistingFiles::Edit) { + // A version made on Make never edits the project picked here. + (d.folder, d.folder_files, d.existing) = (None, None, None); + } } ui.add_space(18.0); if app.state.make.look { @@ -523,5 +532,7 @@ mod tests { d.request = "a snake game".into(); assert_eq!(resolve_target(&d), TargetRef::None); assert_eq!(normalize_url("http://x.y"), "http://x.y"); + let own = MakeDraft { mine: true, request: "Link the footer to https://example.com".into(), ..Default::default() }; + assert_eq!(resolve_target(&own), TargetRef::None, "an address in the request isn't an original"); } } diff --git a/crates/ui-egui/tests/smoke.rs b/crates/ui-egui/tests/smoke.rs index ed3d02f5..493db0c7 100644 --- a/crates/ui-egui/tests/smoke.rs +++ b/crates/ui-egui/tests/smoke.rs @@ -2056,15 +2056,31 @@ fn the_front_door_works_on_a_project_the_person_already_has() { assert_eq!(h.state().state.make.existing, Some(ExistingFiles::Edit)); assert_eq!(h.state().settings.existing_files, None, "the answer in Settings is left alone"); h.get_by_label("Pick another folder…"); + + // Make never edits the folder picked here, and coming back asks for it again. + h.get_by_label("Make my own version").click(); + h.run_steps(3); + assert!(h.state().state.make.folder.is_none() && h.state().state.make.existing.is_none()); + craft_anything_ui_egui::folders::picked_own(h.state_mut(), mine.path().to_path_buf()); + assert!(h.state().state.make.folder.is_none(), "a dialog that ends after leaving the door is dropped"); + h.get_by_label("Work on my project").click(); + h.run_steps(3); + h.get_by_label("Choose your project's folder…"); + craft_anything_ui_egui::folders::picked_own(h.state_mut(), mine.path().to_path_buf()); + h.state_mut().state.make.request = "Link the footer to https://example.com".into(); + h.run_steps(3); h.get_by_label("Start").click(); assert!(wait(&mut h, |a| a.creating.is_none() && a.workspace.is_some()), "made on a worker"); let ws = Workspace::open(mine.path()).expect("the project is the person's folder"); assert!(ws.manifest.app_in_root && ws.app_dir() == mine.path()); + assert_eq!(ws.manifest.target, TargetRef::None, "an address in the request isn't an original"); assert_eq!(ws.manifest.name, mine.path().file_name().unwrap().to_string_lossy(), "named after its folder"); assert_eq!(std::fs::read_to_string(mine.path().join("index.html")).unwrap(), "
hi
"); assert_eq!(runs.started(), [mine.path().to_path_buf()]); assert_eq!(std::fs::read_dir(d.path()).map(|r| r.count()).unwrap_or(0), 0, "nothing in the projects folder"); + // Back on the door, the same folder is a project now, so it opens. + h.state_mut().state.make.mine = true; craft_anything_ui_egui::folders::picked_own(h.state_mut(), mine.path().to_path_buf()); h.run_steps(2); assert!(h.state().state.toasts.iter().any(|(text, err)| !*err && text.contains("already a project"))); diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index c3327c31..804f5ddb 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -635,6 +635,9 @@ Gaps found while using the app for real projects. refused and a folder that's already a project opens. Until now that was only reachable through "Choose a folder…" under Make's Start, on a screen that asks what to recreate — claimed: xerenon, branch feature/r2.9-work-on-my-project; PR #686 +- [x] Leaving "Work on my project" forgets a folder it would edit in place and drops a folder + dialog that ends later, so a version made on Make can't edit that project, and an address + in its request isn't read as an original to recreate (review of #686) — claimed: xerenon, branch bugfix/r2.9-own-project-review; PR #750 - [x] Choose Claude Code's folder (`CLAUDE_CONFIG_DIR`) per AI, so one computer can build with two Claude accounts; an app opened from the Dock never sees the shell's variable. Settings → AI (a typed folder, and Add another) and `ai folder`; live-checked from the CLI with an empty diff --git a/docs/pitfalls.md b/docs/pitfalls.md index db76ad2e..92767df0 100644 --- a/docs/pitfalls.md +++ b/docs/pitfalls.md @@ -58,6 +58,11 @@ whenever a `Cargo.toml` changed. on a new branch, open a new PR that says it supersedes the old one, and comment on the old one. Don't close or rewrite other people's work without asking. +**Review fixes pushed to a PR that had already merged.** #686 was merged hours before its review +comments were acted on; the fixes went to its branch and the threads were answered "Fixed", while +`main` had the code without them. Read `gh pr view --json state,headRefOid` before pushing to a PR's +branch, and land anything late in a new PR from `main`. + ## Tests **A test started a real download on a contributor's machine.** A test called `engines_install` for