Files
vectorcraft/crates/ui-egui/src/dialogs/text_export.rs
bflatasticandClaude Opus 5.5 da18cf4234 M4.50: WebP options, Export As Text and Save for Office Documents
- WebP takes lossless and quality; there is no permissive pure-Rust lossy encoder yet, so
  lossless: false writes lossless with a warning (the WebP Options dialog says so).
- document.export {format: "txt", encoding: utf8|utf16, lineEndings: lf|crlf,
  selectionOnly}: the stories in stacking order (back to front), a thread once, hidden
  objects and template layers left out; UTF-16 is little-endian with a byte order mark.
  Export As → Text opens Text Export Options.
- File → Save for Office Documents… (document.exportForOffice {ppi, transparent, artboard,
  path}): the artboard as a PNG at 72/150/300 ppi on white or transparent, through a small
  dialog in the app.
document.formats' writable list and the MCP export tool's enum gain txt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 11:27:22 +07:00

53 lines
2.3 KiB
Rust

//! Export As → Text: the Text Export Options (encoding, line endings, selection only) for a
//! picked `.txt` path. Fields are `document.export` params (`format`, `path`…).
use serde_json::{Value, json};
use super::png_options::choice;
use super::{DialogSpec, form};
use crate::state::Dialog;
use crate::theme::Tokens;
use crate::{VectorcraftApp, io};
pub(crate) const KIND: &str = "txtOptions";
pub(super) const SPEC: DialogSpec =
DialogSpec { heading: |_| "Text Export Options".into(), body, confirm, ok: Some("Export"), min_width: 340.0, ..DialogSpec::FORM };
const ENCODINGS: [&str; 2] = ["utf8", "utf16"];
const ENCODING_LABELS: [&str; 2] = ["UTF-8", "UTF-16 (Unicode)"];
const LINE_ENDINGS: [&str; 2] = ["lf", "crlf"];
const LINE_ENDING_LABELS: [&str; 2] = ["LF (macOS, Linux)", "CRLF (Windows)"];
/// Open the options for exporting the document's text to `path`.
pub fn open(app: &mut VectorcraftApp, path: &str) {
// The platform's own line endings by default.
let eol = if cfg!(windows) { LINE_ENDINGS[1] } else { LINE_ENDINGS[0] };
let fields = json!({"format": "txt", "path": path, "encoding": ENCODINGS[0], "lineEndings": eol, "selectionOnly": false});
app.ui.dialog = Some(Dialog::new(KIND, fields));
}
fn body(app: &mut VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog) -> bool {
let t = Tokens::get(ui.ctx());
let label = |ui: &mut egui::Ui, text: &str| ui.label(egui::RichText::new(text).color(t.text_dim));
egui::Grid::new("text-export").num_columns(2).spacing([10.0, 8.0]).show(ui, |ui| {
label(ui, "Encoding:");
choice(ui, d, "encoding", &ENCODINGS, &ENCODING_LABELS);
ui.end_row();
label(ui, "Line Endings:");
choice(ui, d, "lineEndings", &LINE_ENDINGS, &LINE_ENDING_LABELS);
ui.end_row();
});
ui.add_space(8.0);
let selected = app.session.active().is_some_and(|st| !st.selection.objects.is_empty());
ui.add_enabled_ui(selected, |ui| form::check(ui, d, "selectionOnly", "Selection Only"));
false
}
/// Write the text file (the command runs first, so a bad option keeps the dialog open).
fn confirm(app: &mut VectorcraftApp, d: &Dialog) -> Result<Value, String> {
let path = io::save_command_output(app, "document.export", "txt", form::params(d))?;
app.ui.dialog = None;
Ok(json!({ "path": path }))
}