Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ sha2 = "0.10.8"
toml = "0.8"
live-server = "0.10.0"
notify = "8.0"
indoc = "2.0.6"
indoc = "2"
ignore = "0.4"

[dev-dependencies]
assert_cmd = "2"
Expand Down
10 changes: 9 additions & 1 deletion examples/first.typ
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@

#toolbox.pdfpc.speaker-note("
What if you could easily generate videos from text?

I think that would be pretty cool.
")
]


#slide[
#set page(fill: black, margin: 3em)
#set text(fill: white)
Expand All @@ -35,7 +38,12 @@
]

#toolbox.pdfpc.speaker-note("
That would be pretty cool.
Here is a plan to make it happen.

Step 1 is easy. Generate videos.

Step 2 is for you to figure out.

Step 3 is profit.
")
]
6 changes: 5 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,14 +117,18 @@ pub(crate) struct WatchArgs {
/// Port to run the server on.
#[arg(long, default_value = "8080")]
port: u16,

/// Command to run before compiling the Typst file.
#[arg(long)]
pre_typst: Option<String>,
}

#[derive(Clone, Debug, clap::Subcommand)]
enum Task {
/// Build the video.
Build(BuildArgs),

/// Watch the input file and rebuild the video when it changes.
/// Watch the current directory and rebuild the video on change.
Watch(WatchArgs),
}

Expand Down
60 changes: 52 additions & 8 deletions src/watch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::build;
use crate::slide::Slide;
use crate::Arguments;
use crate::WatchArgs;
use ignore::Walk;
use live_server::listen;
use notify::recommended_watcher;
use notify::Event;
Expand Down Expand Up @@ -139,14 +140,47 @@ fn remove_old_files(args: &Arguments, timestamp: u64) {
}
}

#[derive(Clone, Debug, PartialEq)]
/// Status of the command.
///
/// This can be used to avoid crashing the watch loop completely. Instead,
/// report an error and ignore further actions until the loop is called again.
/// This allows the user to fix the problem and continue without having to
/// manually restart the `trv watch`.
enum Status {
Success,
Failure,
}

fn run_pre_typst(watch_args: &WatchArgs) -> Status {
if let Some(pre_typst) = &watch_args.pre_typst {
tracing::info!("Running pre-typst command...");
let mut cmd = std::process::Command::new("/usr/bin/env");
cmd.arg("bash");
cmd.arg("-c");
cmd.arg(pre_typst);
let output = cmd.output().expect("Failed to run pre-typst command");
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
tracing::error!("pre-typst command failed: {}", stderr.trim());
return Status::Failure;
}
}
Status::Success
}

async fn watch_build(watch_args: &WatchArgs, args: &Arguments) {
let release = false;
let input = watch_args.input.clone();
let audio_codec = None;
let slides = build(input.clone(), args, release, audio_codec).await;
let timestamp = move_files_into_public(args, &slides);
build_index(args, &slides, timestamp, false);
remove_old_files(args, timestamp);

let status = run_pre_typst(watch_args);
if status == Status::Success {
let slides = build(input.clone(), args, release, audio_codec).await;
let timestamp = move_files_into_public(args, &slides);
build_index(args, &slides, timestamp, false);
remove_old_files(args, timestamp);
}
}

fn spawn_server(watch_args: &WatchArgs, args: &Arguments) {
Expand All @@ -172,10 +206,20 @@ fn spawn_server(watch_args: &WatchArgs, args: &Arguments) {
pub async fn watch(watch_args: &WatchArgs, args: &Arguments) {
let (tx, rx) = mpsc::channel::<Result<Event>>();
let mut watcher = recommended_watcher(tx).unwrap();
let input = watch_args.input.clone();
watcher
.watch(&input, notify::RecursiveMode::NonRecursive)
.expect("Failed to watch");
let mode = notify::RecursiveMode::NonRecursive;
// Watch the current directory since that is probably the most intuitive
// path to watch. It also would allow watching scripts that are in a
// directory that is above the Typst file. For Typst, files have to be in
// the same directory, but allowing the current directory gives more
// flexibility.
//
// Flatten ignores the errors (e.g., permission errors).
for entry in Walk::new("./").flatten() {
let path = entry.path();
if !path.is_dir() {
watcher.watch(path, mode).expect("Failed to watch");
}
}

let public_path = public_dir(args);
if !public_path.exists() {
Expand Down
Loading