Add board presets, devices --clear, and test/UX fixes

Board presets:
- anvil new --board mega (uno, mega, nano, nano-old, leonardo, micro)
- anvil new --list-boards shows presets with compatible clones
- FQBN and baud rate flow into .anvil.toml via template variables
- Defaults to uno when --board is omitted

Devices --clear:
- anvil devices --clear deletes .anvil.local, reverts to auto-detect
This commit is contained in:
Eric Ratliff
2026-02-19 07:41:12 -06:00
parent d7c6d432f9
commit 2739d83b99
9 changed files with 384 additions and 12 deletions

View File

@@ -274,6 +274,31 @@ pub fn set_port(port: Option<&str>, project_dir: Option<&str>) -> Result<()> {
// -- Helpers --------------------------------------------------------------
/// Delete .anvil.local from the given project directory.
pub fn clear_port(project_dir: Option<&str>) -> Result<()> {
let project_path = resolve_project_dir(project_dir)?;
require_anvil_project(&project_path)?;
let local_file = project_path.join(".anvil.local");
if !local_file.exists() {
println!(
"{} No .anvil.local file found -- nothing to clear.",
"--".bright_black()
);
return Ok(());
}
fs::remove_file(&local_file)
.context("Failed to delete .anvil.local")?;
println!(
"{} Removed .anvil.local -- port will be auto-detected.",
"ok".green()
);
Ok(())
}
fn resolve_project_dir(project_dir: Option<&str>) -> Result<PathBuf> {
match project_dir {
Some(dir) => Ok(PathBuf::from(dir)),

View File

@@ -2,6 +2,7 @@ use anyhow::{Result, bail};
use colored::*;
use std::path::PathBuf;
use crate::board::presets::{self, BoardPreset};
use crate::templates::{TemplateManager, TemplateContext};
use crate::version::ANVIL_VERSION;
@@ -28,7 +29,52 @@ pub fn list_templates() -> Result<()> {
Ok(())
}
pub fn create_project(name: &str, template: Option<&str>) -> Result<()> {
pub fn list_boards() -> Result<()> {
println!("{}", "Available board presets:".bright_cyan().bold());
println!();
for preset in presets::PRESETS {
let marker = if preset.name == presets::DEFAULT_PRESET {
" (default)"
} else {
""
};
println!(
" {}{}",
preset.name.bright_white().bold(),
marker.bright_cyan()
);
println!(" {}", preset.description);
if !preset.also_known_as.is_empty() {
println!(" Also: {}", preset.also_known_as.bright_black());
}
println!(" FQBN: {}", preset.fqbn.bright_black());
println!();
}
println!(
" {}",
"Usage: anvil new <project-name> --board mega".bright_yellow()
);
println!();
println!(
" {}",
"For boards not listed here, create a project and edit the".bright_black()
);
println!(
" {}",
"fqbn value in .anvil.toml to any valid arduino-cli FQBN.".bright_black()
);
println!();
Ok(())
}
pub fn create_project(
name: &str,
template: Option<&str>,
board: Option<&str>,
) -> Result<()> {
// Validate project name
validate_project_name(name)?;
@@ -52,6 +98,25 @@ pub fn create_project(name: &str, template: Option<&str>) -> Result<()> {
bail!("Invalid template");
}
// Resolve board preset
let preset: &BoardPreset = match board {
Some(b) => {
match presets::find_preset(b) {
Some(p) => p,
None => {
println!(
"{}",
format!("Unknown board preset: '{}'", b).red().bold()
);
println!();
list_boards()?;
bail!("Invalid board preset");
}
}
}
None => presets::find_preset(presets::DEFAULT_PRESET).unwrap(),
};
println!(
"{}",
format!("Creating Arduino project: {}", name)
@@ -59,6 +124,14 @@ pub fn create_project(name: &str, template: Option<&str>) -> Result<()> {
.bold()
);
println!("{}", format!("Template: {}", template_name).bright_cyan());
println!(
"{}",
format!("Board: {} ({})", preset.name, preset.description).bright_cyan()
);
println!(
"{}",
format!("FQBN: {}", preset.fqbn).bright_black()
);
println!();
// Create project directory
@@ -69,6 +142,8 @@ pub fn create_project(name: &str, template: Option<&str>) -> Result<()> {
let context = TemplateContext {
project_name: name.to_string(),
anvil_version: ANVIL_VERSION.to_string(),
fqbn: preset.fqbn.to_string(),
baud: preset.baud,
};
let file_count = TemplateManager::extract(template_name, &project_path, &context)?;

View File

@@ -47,6 +47,8 @@ pub fn run_refresh(project_dir: Option<&str>, force: bool) -> Result<()> {
let context = TemplateContext {
project_name: config.project.name.clone(),
anvil_version: ANVIL_VERSION.to_string(),
fqbn: config.build.fqbn.clone(),
baud: config.monitor.baud,
};
// Extract template into a temp directory so we can compare