dioxus-mind-elixir 0.1.1

A Rust-native Dioxus mind map component with CSS-variable styling
Documentation

dioxus-mind-elixir

English · 简体中文 · crates.io · API reference

A Rust + Dioxus component version and fork of Mind Elixir Core, rewritten in Rust from its data model and interaction design. Visit the original Mind Elixir website. Thank you to its authors for the foundation.

AI policy: This project is entirely AI-generated and has undergone human regression testing. If you object to AI-generated software, please do not use this project. AI-generated merge requests (MRs/PRs) are welcome; no AI disclosure label is required. Contributions still need to meet the project's quality and testing requirements.

What it provides

  • Editable plain-text mind maps with left, right, both-side and downward layouts.
  • Checked tree operations, shared document/selection, atomic edits and undo/redo.
  • Keyboard commands, multi-selection, marquee, drag/drop, context menu and toolbar through optional Rust plugins.
  • An outline that shares the map's document and history, with English/Chinese command labels.
  • Inheritable --dme-* CSS variables for nodes, connectors, editor and plugins.
  • Strict JSON persistence, plaintext conversion and modern XMind export.
  • Web live CSS/font capture to SVG, PNG, JPEG, WebP and static HTML.

This is a Rust rewrite, not a drop-in implementation of every upstream API or plugin. Unknown JSON fields are rejected. See compatibility and platform limits.

Requirements

Dependency Version / requirement
Rust 1.85+, edition 2024
dioxus-mind-elixir 0.1.0 on crates.io
Dioxus 0.7.10, matching the version used to build/test this release
Dioxus CLI (dx) 0.7.10 for running/building an application
Web target wasm32-unknown-unknown
Desktop Dioxus desktop renderer and your platform's native prerequisites

No npm packages or upstream JavaScript plugins are needed. Project-maintained functionality is Rust. Dioxus and the WebView/WASM toolchain provide their own runtime and generated JavaScript bindings.

Install from Cargo: Web

Create an application and add the published crate:

cargo new my-mind-map
cd my-mind-map
cargo add dioxus@=0.7.10 --no-default-features --features macro,html,signals,hooks,mounted,launch,web
cargo add dioxus-mind-elixir@0.1.0 --features web
rustup target add wasm32-unknown-unknown
cargo install dioxus-cli --version 0.7.10 --locked

Equivalent Cargo.toml:

[package]
name = "my-mind-map"
version = "0.1.0"
edition = "2024"
rust-version = "1.85"

[dependencies]
dioxus = { version = "=0.7.10", default-features = false, features = ["macro", "html", "signals", "hooks", "mounted", "launch", "web"] }
dioxus-mind-elixir = { version = "0.1.0", features = ["web"] }

Put this in src/main.rs:

use dioxus::prelude::*;
use dioxus_mind_elixir::{
    EditableMindMap, MindMapData, MindMapError, Node, Outline, Toolbar,
    plugins::{Locale, Plugin},
    use_mind_map, use_plugins,
};

fn main() {
    dioxus::launch(App);
}

#[component]
fn App() -> Element {
    let handle = match use_mind_map(MindMapData::new(
        Node::new("root", "Ideas").with_children(vec![
            Node::new("rust", "Build with Rust"),
            Node::new("style", "Make it yours"),
        ]),
    )) {
        Ok(handle) => handle,
        Err(error) => return rsx! { p { role: "alert", "{error}" } },
    };
    let _plugins = match use_plugins(handle, Plugin::builtins()) {
        Ok(plugins) => plugins,
        Err(error) => return rsx! { p { role: "alert", "{error}" } },
    };
    let mut error = use_signal(String::new);
    rsx! {
        main {
            style: "--dme-root-background:#315c46;--dme-node-radius:8px;",
            Toolbar { handle, locale: Locale::En }
            EditableMindMap {
                handle,
                locale: Locale::En,
                style: "height:500px;",
                on_error: move |value: MindMapError| error.set(value.to_string()),
            }
            Outline { handle, locale: Locale::En }
            p { role: "alert", "{error}" }
        }
    }
}

Run dx serve --platform web. Styles are embedded in the crate; there is no asset-copy step. Keep the guard returned by use_plugins alive. The toolbar and outline appear when their capabilities are installed; omit plugins to use core editing alone. Set Locale::Zh for Chinese command labels.

Desktop

Use the same component code, but choose the Desktop renderer instead of Web:

[dependencies]
dioxus = { version = "=0.7.10", default-features = false, features = ["macro", "html", "signals", "hooks", "mounted", "launch", "desktop"] }
dioxus-mind-elixir = "0.1.0"

Run dx serve --platform desktop. The library has no default renderer. Do not enable its web feature for a Desktop application. Desktop live image/HTML capture is currently unsupported and returns a recoverable error; tree editing, JSON, XMind and host-provided file saving remain available.

Data, operations and plugins

use_mind_map creates a checked shared MindMapHandle. Pass it to multiple views to share document, selection and history. Viewports and outline browsing stay local to each view. For read-only rendering, use MindMap with a ReadSignal<MindMapData>; for non-UI operations, use MindMapState.

These operations belong in an event handler or service, not during a component render:

use dioxus_mind_elixir::{LayoutDirection, MindMapData, MindMapError, MindMapHandle, Operation};

fn rename_and_save(handle: MindMapHandle) -> Result<String, MindMapError> {
    handle.apply(Operation::SetTopic { id: "rust".into(), topic: "Rust + Dioxus".into() })?;
    handle.apply(Operation::SetLayout(LayoutDirection::Side))?;
    handle.document()?.to_json()
}

fn load_json(handle: MindMapHandle, json: &str) -> Result<(), MindMapError> {
    handle.refresh(MindMapData::from_json(json)?)
}

handle.undo(), redo(), select(), set_read_only() and apply() return Result. refresh replaces the document and resets history. Subscribe with handle.subscribe() and retain the returned guard. Plugin::builtins() enables the standard capabilities; register_plugin, set_plugin_enabled and RAII guards control per-document registration and lifetime. No global registry or dynamic script loader is involved.

In the map: double-click/F2 edits, Enter adds a sibling, Tab adds a child, Ctrl/⌘+Z undoes, Ctrl/⌘+click selects multiple nodes and Shift+drag on blank space selects a region. While editing, Enter/Tab commits, Shift+Enter adds a newline and Escape cancels.

Styling

Place variables on a common parent to style the map, toolbar and outline together:

.my-map-host {
  --dme-root-background: #67518c;
  --dme-font-size: 18px;
  --dme-node-radius: 6px;
  --dme-gap-x: 64px;
  --dme-link-color: #b5a0d0;
  --dme-toolbar-background: #f3edf9;
  --dme-outline-background: #f3edf9;
}

Defaults preserve inheritance, so separate hosts can have independent themes. Explicit node styles or a Theme prop can override inherited values. Geometry uses measured DOM sizes; increase measurement_revision for external position-only changes. See the complete CSS variable reference (Chinese, with CSS names and defaults).

Export

plugins::export::xmind(&handle.document()?) returns an ExportFile. Use file.save_with(...) for a host-owned filesystem/dialog sink, or file.download() on Web. JSON is the complete persistence format; XMind and plaintext have documented field limits.

For Web images, retain event.data() from a wrapper's onmounted event as Rc<MountedData>, then call from an async event handler:

use dioxus::prelude::MountedData;
use dioxus_mind_elixir::{MindMapError, plugins::export};

async fn download_png(element: &MountedData) -> Result<(), MindMapError> {
    let options = export::ImageOptions::default();
    let svg = export::capture_svg(element, &options).await?;
    export::rasterize(&svg, export::ImageFormat::Png, &options).await?.download()
}

The example requires web and a WASM target. Capture an idle mounted map; readable/embedded fonts and resources are required. static_html(title, &svg) produces an offline static view, not an editable app. See export and platform boundaries.

Playground and GitHub Pages

The maintained public example lives in demo; core/plugin regression applications remain separate under tests/.

cd demo
dx serve --platform web
# Stop the preview, then build from the repository root:
cd ..
scripts/build-pages /dioxus-mind-elixir/

Deploy dist/pages/ in full. Use / as the build argument for a custom domain or user site. The demo has English/Chinese UI, prominent Mind Elixir attribution, editing, shared outline, themes and downloads. ?lang=zh opens Chinese; language switching preserves your document. Edits are in memory: download JSON before reloading.

A GitHub Pages workflow is included. Select GitHub Actions under repository Settings → Pages. Every release deploys the playground from the new tag; run the workflow manually to redeploy (optionally from release or a tag). The workflow reads the hosting base path automatically. The bundle needs HTTP(S), not file://, to load WASM.

Repository layout

src/                 Component library
assets/              Embedded component stylesheet
demo/                The only public demo; also the GitHub Pages source
tests/
  foundation.rs      Library integration tests (Cargo convention)
  core/              Independent core regression application and tests
    browser/         Rust WebDriver runner for the core application
  plugins/           Independent plugin regression application and tests
    browser/         Rust WebDriver runner for the plugin application
docs/                CSS reference and compatibility limits
scripts/             Build and test entry points

demo/ is for users; the two applications inside tests/ are regression fixtures, not additional examples. Build output stays in ignored target/ and dist/ directories.

Development

cargo fmt --all -- --check
cargo clippy -p dioxus-mind-elixir --all-targets --locked -- -D warnings
cargo test -p dioxus-mind-elixir --locked
cargo test -p mind-elixir-core-tests --locked
cargo test -p mind-elixir-plugin-tests --locked
cargo doc -p dioxus-mind-elixir --no-deps --locked

The independent core/plugin applications and their Rust browser runners are all under tests/. See the testing guide. Project code forbids unsafe Rust and denies production unwrap, expect and panic in Clippy checks.

Releases

Merging into main releases automatically (release workflow):

  1. CI runs in full; a failure stops the release.
  2. The version is chosen by scripts/release-version. If the merged branch changed version in Cargo.toml, that version is released. If Cargo.toml still names a version that has not been released, that version is used. Otherwise the patch of the latest release (tag or crates.io) is increased by one. The version must be newer than every released one.
  3. One commit release: vX.Y.Z (version, Cargo.lock, dated CHANGELOG.md heading) is added on top of the merged commit, tagged vX.Y.Z and pushed to the release branch. main itself is never rewritten.
  4. The tag is published to crates.io, a GitHub release is created from the matching CHANGELOG.md section, and the playground is deployed to Pages.

Write changes under ## X.Y.Z — unreleased (or ## Unreleased) in CHANGELOG.md. A run can be repeated safely: existing tags, crates.io versions and GitHub releases are reused. Run workflow accepts an explicit version. Repository setup: a CARGO_REGISTRY_TOKEN repository secret (a crates.io token with the publish-update scope) and Settings → Actions → Workflow permissions set to read and write. The workflow replaces the release branch on every release, so do not commit to it by hand.

Chromium regressions and macOS runtime/manual checks have been performed. Safari, Windows/Linux Desktop, real OS IME candidate confirmation and commercial XMind application checks remain unverified. See compatibility.

Attribution and license

MIT: project license, upstream license, NOTICE. Upstream source and plugin references are recorded in NOTICE. See the changelog. This project is not an official Mind Elixir release.