Skip to content

Commit eb2a91c

Browse files
committed
support cli usage
1 parent 77003d5 commit eb2a91c

File tree

7 files changed

+155
-76
lines changed

7 files changed

+155
-76
lines changed

Cargo.lock

Lines changed: 7 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[workspace.package]
2-
description = "A Tauri App"
2+
description = "A LyX-like experience for Typst"
33
authors = ["Noam Zaks", "Myriad-Dreamin", "TyX Developers"]
44
version = "0.1.0"
55
rust-version = "1.88.0"
@@ -9,7 +9,6 @@ license = "MIT"
99
homepage = "https://tyx-editor.com/"
1010
repository = "https://github.com/tyx-editor/TyX"
1111

12-
1312
[workspace]
1413
resolver = "2"
1514
members = ["crates/*", "src-tauri"]
@@ -20,20 +19,15 @@ opt-level = 3
2019
panic = "abort"
2120

2221
[workspace.dependencies]
23-
2422
cmark-writer = { version = "0.6.3", features = ["gfm"] }
2523
ecow = { version = "0.2", features = ["serde"] }
26-
2724
serde = { version = "1", features = ["derive"] }
2825
serde_json = "1"
29-
3026
insta = "1"
31-
3227
tyx-schema = { path = "crates/tyx-schema", version = "0.1.0" }
3328
tyx-converters = { path = "crates/tyx-converters", version = "0.1.0" }
3429
tyx-to-typst = { path = "crates/tyx-to-typst", version = "0.1.0" }
3530
typst-to-tyx = { path = "crates/typst-to-tyx", version = "0.1.0" }
36-
3731
tinymist-project = "0.14.0"
3832
tinymist-std = "0.14.0"
3933
tinymist-task = "0.14.0"
@@ -49,7 +43,6 @@ broken_intra_doc_links = "warn"
4943

5044
[workspace.lints.rust]
5145
missing_docs = "warn"
52-
5346
unexpected_cfgs = { level = "allow", check-cfg = [
5447
'cfg(wasm_bindgen_unstable_test_coverage)',
5548
'cfg(noop)',
@@ -74,7 +67,6 @@ ignore-hidden = false
7467
extend-exclude = ["/.git", "fixtures"]
7568

7669
[patch.crates-io]
77-
7870
# todo: remove these typst patch?
7971
# A regular build MUST use `tag` or `rev` to specify the version of the patched crate to ensure stability.
8072
typst = { git = "https://github.com/Myriad-Dreamin/typst.git", tag = "tinymist/v0.14.0-rc1" }

crates/tyx-to-typst/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ fn tyx_document_settings_to_typst(settings: &Option<TyXDocumentSettings>) -> Str
365365
}
366366
if let Some(language) = &settings.language {
367367
result += &format!(
368-
"#set page(lang: {})\n",
368+
"#set text(lang: {})\n",
369369
serde_json::to_string(language).unwrap()
370370
);
371371
}

src-tauri/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ crate-type = ["staticlib", "cdylib", "rlib"]
1818

1919
[build-dependencies]
2020
tauri-build = { version = "2", features = [] }
21+
serde.workspace = true
22+
serde_json.workspace = true
2123

2224
[dependencies]
2325
serde.workspace = true
@@ -35,6 +37,7 @@ url = "2.5.4"
3537
open = "5"
3638
base64 = "0.22"
3739
dunce = "1.0.5"
40+
clap = "4.5.53"
3841

3942
[target."cfg(not(any(target_os = \"android\", target_os = \"ios\")))".dependencies]
4043
tauri-plugin-updater = "2"

src-tauri/build.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
11
fn main() {
2+
let config = std::fs::read_to_string("tauri.conf.json").expect("tauri.conf.json not found");
3+
let version = serde_json::from_str::<serde_json::Value>(&config)
4+
.ok()
5+
.and_then(|json| json.get("version")?.as_str().map(|s| s.to_string()))
6+
.expect("version not found in tauri.conf.json");
7+
std::fs::write(
8+
"src/version.rs",
9+
format!("pub(crate) const VERSION: &str = \"{version}\";"),
10+
)
11+
.unwrap();
212
tauri_build::build()
313
}

src-tauri/src/lib.rs

Lines changed: 132 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,33 @@ use tauri::{
1212
menu::{AboutMetadata, MenuBuilder, MenuItemBuilder, SubmenuBuilder},
1313
};
1414

15+
use clap::{Parser, ValueEnum};
1516
use tauri_plugin_dialog::DialogExt;
1617
use tinymist_project::{
1718
CompileFontArgs, CompileOnceArgs, EntryReader, TaskInputs, WorldProvider, base::ShadowApi,
1819
};
1920
use typst_pdf::PdfOptions;
2021
use typstyle_core::Typstyle;
2122

23+
mod version;
24+
25+
#[derive(Debug, Clone, ValueEnum)]
26+
enum OutputFormat {
27+
Typst,
28+
PDF,
29+
}
30+
31+
/// Simple program to greet a person
32+
#[derive(Parser, Debug)]
33+
#[command(version = version::VERSION, about, long_about = None, display_name="TyX")]
34+
struct Args {
35+
/// Files to open or export.
36+
files: Vec<String>,
37+
/// Export as this file format.
38+
#[arg(short, long)]
39+
export: Option<OutputFormat>,
40+
}
41+
2242
#[tauri::command]
2343
fn save(filename: &str, content: &str, format: bool) {
2444
let mut content = String::from(content);
@@ -43,13 +63,14 @@ fn saveas(handle: tauri::AppHandle) {
4363
.add_filter("TyX", &["tyx"])
4464
.save_file(move |f| {
4565
if let Some(f) = f
46-
&& let Some(path) = f.as_path() {
47-
let mut path = path.to_str().unwrap().to_string();
48-
if !path.ends_with(".tyx") {
49-
path += ".tyx";
50-
}
51-
h.emit("saveas", (path,)).unwrap();
66+
&& let Some(path) = f.as_path()
67+
{
68+
let mut path = path.to_str().unwrap().to_string();
69+
if !path.ends_with(".tyx") {
70+
path += ".tyx";
5271
}
72+
h.emit("saveas", (path,)).unwrap();
73+
}
5374
});
5475
}
5576

@@ -110,9 +131,10 @@ fn open(handle: tauri::AppHandle, filename: &str) {
110131
.add_filter("Typst", &["typ"])
111132
.pick_file(move |f| {
112133
if let Some(f) = f
113-
&& let Some(path) = f.as_path() {
114-
openfile(&h, path, true);
115-
}
134+
&& let Some(path) = f.as_path()
135+
{
136+
openfile(&h, path, true);
137+
}
116138
});
117139
}
118140

@@ -131,9 +153,10 @@ fn newfromtemplate(handle: tauri::AppHandle) {
131153
.set_directory(settings_path)
132154
.pick_file(move |f| {
133155
if let Some(f) = f
134-
&& let Some(path) = f.as_path() {
135-
openfile(&h, path, false);
136-
}
156+
&& let Some(path) = f.as_path()
157+
{
158+
openfile(&h, path, false);
159+
}
137160
});
138161
}
139162

@@ -190,6 +213,57 @@ fn readimage(filename: &str, image: &str) -> String {
190213
String::from("data:") + mimetype + ";base64," + STANDARD.encode(&bytes).as_str()
191214
}
192215

216+
fn typst_to_pdf(
217+
filename: &str,
218+
content: &str,
219+
root_path: PathBuf,
220+
font_paths: Vec<PathBuf>,
221+
) -> Result<Vec<u8>, String> {
222+
let filename_path = PathBuf::from(&filename);
223+
if !filename_path.is_file() {
224+
File::create(&filename_path).unwrap();
225+
}
226+
let filename_path = dunce::canonicalize(filename_path).unwrap();
227+
let universe = CompileOnceArgs {
228+
root: Some(dunce::canonicalize(&root_path).unwrap()),
229+
input: Some(filename_path.to_str().unwrap().to_string()),
230+
font: CompileFontArgs {
231+
font_paths,
232+
..CompileFontArgs::default()
233+
},
234+
..CompileOnceArgs::default()
235+
}
236+
.resolve()
237+
.unwrap();
238+
let entry = match universe
239+
.entry_state()
240+
.try_select_path_in_workspace(&filename_path)
241+
{
242+
Ok(entry) => entry,
243+
Err(_e) => {
244+
return Err("Invalid root directory, couldn't select the file itself!".into());
245+
}
246+
};
247+
let mut world = universe.snapshot_with(Some(TaskInputs {
248+
entry,
249+
..TaskInputs::default()
250+
}));
251+
let main = world.main_id().unwrap();
252+
let _ = world.map_shadow_by_id(
253+
main,
254+
typst::foundations::Bytes::from_string(content.to_owned()),
255+
);
256+
let doc = match typst::compile(&world).output {
257+
Ok(doc) => doc,
258+
Err(e) => {
259+
return Err(e[0].message.to_string());
260+
}
261+
};
262+
let pdf = typst_pdf::pdf(&doc, &PdfOptions::default()).unwrap();
263+
264+
Ok(pdf)
265+
}
266+
193267
#[tauri::command]
194268
fn preview(
195269
handle: tauri::AppHandle,
@@ -237,51 +311,16 @@ fn preview(
237311
dunce::canonicalize(&p).unwrap()
238312
})
239313
.collect::<Vec<PathBuf>>();
314+
240315
font_paths.insert(0, tyx_fonts_path);
241-
let filename_path = PathBuf::from(&filename);
242-
if !filename_path.is_file() {
243-
File::create(&filename_path).unwrap();
244-
}
245-
let filename_path = dunce::canonicalize(filename_path).unwrap();
246-
let universe = CompileOnceArgs {
247-
root: Some(dunce::canonicalize(&root_path).unwrap()),
248-
input: Some(filename_path.to_str().unwrap().to_string()),
249-
font: CompileFontArgs {
250-
font_paths,
251-
..CompileFontArgs::default()
252-
},
253-
..CompileOnceArgs::default()
254-
}
255-
.resolve()
256-
.unwrap();
257-
let entry = match universe
258-
.entry_state()
259-
.try_select_path_in_workspace(&filename_path)
260-
{
261-
Ok(entry) => entry,
262-
Err(_e) => {
263-
return String::from("Invalid root directory, couldn't select the file itself!");
264-
}
265-
};
266-
let mut world = universe.snapshot_with(Some(TaskInputs {
267-
entry,
268-
..TaskInputs::default()
269-
}));
270-
let main = world.main_id().unwrap();
271-
let _ = world.map_shadow_by_id(
272-
main,
273-
typst::foundations::Bytes::from_string(content.to_owned()),
274-
);
275-
let doc = match typst::compile(&world).output {
276-
Ok(doc) => doc,
316+
317+
let pdf = match typst_to_pdf(&filename, content, root_path, font_paths) {
318+
Ok(pdf) => pdf,
277319
Err(e) => {
278-
if e.is_empty() {
279-
return String::new();
280-
}
281-
return e[0].message.to_string();
320+
return e;
282321
}
283322
};
284-
let pdf = typst_pdf::pdf(&doc, &PdfOptions::default()).unwrap();
323+
285324
let mut f = File::create(&pdf_file).unwrap();
286325
f.write_all(&pdf).unwrap();
287326

@@ -308,13 +347,14 @@ fn insertimage(handle: tauri::AppHandle, filename: &str) {
308347
.add_filter("Image", &["png", "jpg", "jpeg", "gif", "svg"])
309348
.pick_file(move |f| {
310349
if let Some(f) = f
311-
&& let Some(path) = f.as_path() {
312-
h.emit(
313-
"insertImage",
314-
(path.strip_prefix(dirname).unwrap_or(path).to_str().unwrap(),),
315-
)
316-
.unwrap();
317-
}
350+
&& let Some(path) = f.as_path()
351+
{
352+
h.emit(
353+
"insertImage",
354+
(path.strip_prefix(dirname).unwrap_or(path).to_str().unwrap(),),
355+
)
356+
.unwrap();
357+
}
318358
});
319359
}
320360

@@ -354,6 +394,38 @@ pub fn run() {
354394

355395
#[cfg(not(any(target_os = "android", target_os = "ios")))]
356396
pub fn run() {
397+
let args = Args::parse();
398+
399+
if let Some(format) = args.export {
400+
for file in args.files {
401+
let file_base = match file.strip_suffix(".tyx") {
402+
Some(file) => file,
403+
None => {
404+
println!("warning: file {file} might not be a TyX document!");
405+
&file
406+
}
407+
};
408+
let dirname = Path::new(&file).parent().unwrap().to_str().unwrap();
409+
let contents = std::fs::read_to_string(&file).unwrap();
410+
let result = match format {
411+
OutputFormat::Typst => {
412+
tyx_converters::serialized_tyx_to_typst(&contents).into_bytes()
413+
}
414+
OutputFormat::PDF => {
415+
let contents = tyx_converters::serialized_tyx_to_typst(&contents);
416+
typst_to_pdf(&file, &contents, PathBuf::from(dirname), vec![]).unwrap()
417+
}
418+
};
419+
let file_extension = match format {
420+
OutputFormat::Typst => ".typ",
421+
OutputFormat::PDF => ".pdf",
422+
};
423+
424+
std::fs::write(String::from(file_base) + file_extension, result).unwrap();
425+
}
426+
return;
427+
}
428+
357429
tauri::Builder::default()
358430
.plugin(tauri_plugin_process::init())
359431
.plugin(tauri_plugin_updater::Builder::new().build())

src-tauri/src/version.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub(crate) const VERSION: &str = "0.2.15";

0 commit comments

Comments
 (0)