Files
Marcos Paulo 3b086de6cf
ci / ci (macos-latest) (push) Has been cancelled
ci / ci (ubuntu-latest) (push) Has been cancelled
ci / ci (windows-latest) (push) Has been cancelled
feat(ui): select text in the Visual view with a floating toolbar (copy, cut, highlight, bold, italic, strike, code, link, ask/propose via assistant, search)
Drag across rows, double click a word, triple click a row; translucent accent wash; toolbar pops in above the
selection. Editing actions map the rendered text to its markdown source and are disabled where they cannot.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-16 14:11:56 -03:00

58 lines
2.8 KiB
Rust

//! Renders the Visual view with a text selection and its floating toolbar offscreen:
//! `cargo run -p notescraft-ui-egui --example selection_view -- out.png [--theme id]`.
use notescraft_ui_egui::{note_view, theme::Tokens};
const SOURCE: &str = "# NotesCraft spec\n\nA native, Obsidian-compatible vault. Notes link through [[Roadmap]] and stay **plain markdown** on disk.\n\n> [!tip] Tip\n> Every write goes through craft-atomic.\n\n## Why native\n\nSame egui stack as PhotoCraft, no webview, and every action is a command. ==One shared theme layer across the suite.==\n\n- Vault and markdown\n- Editor\n";
fn main() {
let mut args = std::env::args().skip(1);
let out = args.next().unwrap_or_else(|| "/tmp/notescraft-selection.png".to_owned());
let mut theme_id = "catppuccin-mocha".to_owned();
while let Some(flag) = args.next() {
if flag == "--theme" {
theme_id = args.next().unwrap_or(theme_id);
}
}
let Some(theme) = notescraft_themes::builtin(&theme_id) else {
eprintln!("unknown theme {theme_id}");
std::process::exit(2);
};
let tokens = Tokens::new(theme);
let doc = notescraft_markdown::parse(SOURCE);
let mut folds = note_view::CalloutFolds::default();
let mut ready = false;
let mut harness = egui_kittest::Harness::builder().with_size(egui::vec2(860.0, 420.0)).wgpu().build_ui(move |ui| {
// Named font families exist from the next frame on: install them and draw nothing yet.
if !ready {
notescraft_ui_egui::fonts::install(ui.ctx());
egui_extras::install_image_loaders(ui.ctx());
ready = true;
ui.ctx().request_repaint();
return;
}
ui.add_space(12.0);
let _ = note_view::show_note(ui, &tokens, &doc, &|_: &str| true, &mut folds);
});
harness.run();
// Drag from the middle of the first paragraph line to the end of the next block.
let start = egui::pos2(220.0, 87.0);
let end = egui::pos2(520.0, 112.0);
harness.event(egui::Event::PointerMoved(start));
harness.event(egui::Event::PointerButton { pos: start, button: egui::PointerButton::Primary, pressed: true, modifiers: egui::Modifiers::default() });
harness.run();
harness.event(egui::Event::PointerMoved(egui::pos2(300.0, 90.0)));
harness.run();
harness.event(egui::Event::PointerMoved(end));
harness.run();
harness.event(egui::Event::PointerButton { pos: end, button: egui::PointerButton::Primary, pressed: false, modifiers: egui::Modifiers::default() });
harness.run();
match harness.render() {
Ok(image) => match image.save(&out) {
Ok(()) => println!("wrote {out}"),
Err(error) => eprintln!("could not save {out}: {error}"),
},
Err(error) => eprintln!("render failed: {error}"),
}
}