6 stable releases
Uses new Rust 2024
| 3.0.0 | Jan 13, 2026 |
|---|---|
| 2.0.4 | Jan 13, 2026 |
| 1.0.0 | Jan 13, 2026 |
#427 in Development tools
38KB
350 lines
Apple Notes Exporter
A library and CLI tool for recursively exporting Apple Notes folders to the file system via AppleScript. This Rust-based tool allows you to export entire folder hierarchies from Apple Notes, creating a mirrored directory structure with each note saved as an HTML file.
Quick Install
Install from crates.io using Cargo:
cargo install apple-notes-exporter-rs
Or install directly from GitHub:
cargo install --git https://github.com/pRizz/apple-notes-exporter-rs.git
Features
- Recursive export of Apple Notes folders (including all subfolders and notes)
- Automatic image extraction - embedded base64 images are extracted to separate files
- List all available folders across all accounts
- Creates a mirrored directory tree structure
- Exports each note as an HTML file with images in a companion folder
- Breadth-first search (BFS) to find folders at any level
- Support for multiple Apple Notes accounts
- Simple command-line interface with subcommands
- Library API for programmatic access
CLI Installation
Install from crates.io (Recommended)
cargo install apple-notes-exporter-rs
Install from GitHub
Install directly from the GitHub repository:
cargo install --git https://github.com/pRizz/apple-notes-exporter-rs.git
Build from Source
git clone --recursive https://github.com/pRizz/apple-notes-exporter-rs.git
cd apple-notes-exporter-rs
cargo build --release
The binary will be available at target/release/apple-notes-exporter.
CLI Usage
The tool provides three subcommands: list (or ls), export, and extract-attachments.
List Available Folders
List all top-level folders across all Apple Notes accounts:
apple-notes-exporter list
# or
apple-notes-exporter ls
Export a Folder
Export a folder recursively to HTML files. By default, embedded images are automatically extracted to separate files:
apple-notes-exporter export <FOLDER> <OUTPUT_DIR>
This creates a structure like:
exports/
├── My Notes/
│ ├── Note Title -- abc123.html
│ ├── Note Title -- abc123-attachments/
│ │ ├── attachment-001.jpg
│ │ └── attachment-002.png
│ └── Another Note -- def456.html
To skip image extraction (keep images as embedded base64):
apple-notes-exporter export "My Notes" ./exports --no-extract-attachments
Extract Attachments from Existing Exports
If you have previously exported notes without extracting images, you can extract them later:
apple-notes-exporter extract-attachments ./exports
Examples
List all available folders:
apple-notes-exporter list # or: apple-notes-exporter ls
Export a folder (searches all accounts):
apple-notes-exporter export "My Notes" ./exports
Export a folder from a specific account (useful when folder names exist in multiple accounts):
apple-notes-exporter export "iCloud:My Notes" ./exports
apple-notes-exporter export "Google:Work Notes" ./exports
Export without extracting images:
apple-notes-exporter export "My Notes" ./exports --no-extract-attachments
Running from Source
If you want to run the tool directly from the source code without installing:
# Clone the repository with submodules
git clone --recursive https://github.com/pRizz/apple-notes-exporter-rs.git
cd apple-notes-exporter-rs
# List folders
cargo run -- list # or: cargo run -- ls
# Export a folder
cargo run -- export "My Notes" ./exports
If you already cloned without --recursive, initialize the submodules:
git submodule update --init --recursive
Library Usage
Add the dependency to your Cargo.toml:
[dependencies]
apple-notes-exporter-rs = "1.0"
Quick Start
use apple_notes_exporter_rs::{list_folders, export_folder, export_folder_from_account};
fn main() -> apple_notes_exporter_rs::Result<()> {
// List all available folders (prints to stdout)
list_folders()?;
// Export a folder to a directory (searches all accounts)
export_folder("My Notes", "./exports")?;
// Export from a specific account (useful when folder names are duplicated)
export_folder_from_account("iCloud", "Work", "./exports")?;
export_folder_from_account("Google", "Work", "./google_exports")?;
Ok(())
}
Using the Exporter Struct
For more control, use the Exporter struct:
use apple_notes_exporter_rs::Exporter;
fn main() -> apple_notes_exporter_rs::Result<()> {
// Create an exporter with the embedded AppleScript
let exporter = Exporter::new();
exporter.list_folders()?;
// Export and extract images (recommended)
let results = exporter.export_folder_with_attachments("My Notes", "./exports")?;
println!("Extracted {} images", results.iter().map(|r| r.attachments.len()).sum::<usize>());
// Or export without extracting images
exporter.export_folder("Work", "./work_exports")?;
Ok(())
}
Using a Custom AppleScript
If you need to use a modified AppleScript:
use apple_notes_exporter_rs::Exporter;
fn main() -> apple_notes_exporter_rs::Result<()> {
let exporter = Exporter::with_script_path("./custom_script.applescript")?;
exporter.list_folders()?;
exporter.export_folder("My Notes", "./exports")?;
Ok(())
}
Extracting Attachments from Existing Exports
You can also extract images from previously exported HTML files:
use apple_notes_exporter_rs::{extract_attachments_from_html, extract_attachments_from_directory};
fn main() -> apple_notes_exporter_rs::Result<()> {
// Extract from a single HTML file
let result = extract_attachments_from_html("./exports/My Note -- abc123.html")?;
println!("Extracted {} images", result.attachments.len());
// Extract from all HTML files in a directory (recursive)
let results = extract_attachments_from_directory("./exports")?;
let total: usize = results.iter().map(|r| r.attachments.len()).sum();
println!("Extracted {total} images from {} files", results.len());
Ok(())
}
Error Handling
The library provides a custom ExportError type:
use apple_notes_exporter_rs::{export_folder, ExportError};
fn main() {
match export_folder("My Notes", "./exports") {
Ok(()) => println!("Export successful!"),
Err(ExportError::ScriptNotFound(path)) => {
eprintln!("Script not found: {}", path.display());
}
Err(ExportError::ScriptFailed(code)) => {
eprintln!("AppleScript failed with exit code: {}", code);
}
Err(e) => eprintln!("Error: {}", e),
}
}
How It Works
-
Folder Search: The tool uses breadth-first search (BFS) to find the specified folder at any level in your Apple Notes hierarchy (not just top-level folders).
-
Export Process: Once found, it recursively exports that folder and all its subfolders, creating a mirrored directory tree.
-
Output Format: Each note is exported as an HTML file, preserving the folder structure in the output directory.
-
Image Extraction: By default, embedded base64 images in the HTML are extracted to separate files in a companion
<note-name>-attachments/folder. The HTML is updated to reference the local files. Supported formats: PNG, JPEG, GIF, WebP, SVG, BMP, TIFF. -
Account Handling: By default, the folder search looks in all accounts. If a folder name exists in multiple accounts, you can specify the account using the
AccountName:FolderNameformat.
Requirements
macOS Only
This tool is macOS-specific as it relies on AppleScript to interact with the Notes app. While the library can be compiled on any platform, running it on non-macOS systems will return an error at runtime.
Automation Permissions
Important: This tool requires Automation permissions for the Notes app. You'll need to grant these permissions when you first run the tool:
- Go to System Settings > Privacy & Security > Automation
- Find the application that invoked the script (e.g., Terminal, iTerm, or Script Editor)
- Enable permissions for the Notes app
If permissions are not granted, the export will fail.
Project Structure
apple-notes-exporter-rs/
├── src/
│ ├── lib.rs # Library: export API + attachment extraction
│ └── main.rs # CLI application
├── vendor/
│ └── apple-notes-exporter/
│ └── scripts/
│ └── export_notes.applescript # AppleScript used for export
├── Cargo.toml
└── README.md
See Also
- apple-notes-exporter-ts - A TypeScript/Node.js version of this tool
License
This project is licensed under the MIT License - see the LICENSE file for details.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Acknowledgments
This tool uses the AppleScript from the apple-notes-exporter project, which is included as a git submodule.
Dependencies
~5–11MB
~236K SLoC