diff --git a/crates/typst-preview/src/actor/render.rs b/crates/typst-preview/src/actor/render.rs index a454a6bbe..098c06576 100644 --- a/crates/typst-preview/src/actor/render.rs +++ b/crates/typst-preview/src/actor/render.rs @@ -43,6 +43,7 @@ impl RenderActorRequest { pub struct RenderActor { mailbox: broadcast::Receiver, + direct_mailbox: mpsc::UnboundedReceiver, view: Arc>>>, renderer: IncrSvgDocServer, editor_conn_sender: mpsc::UnboundedSender, @@ -53,6 +54,7 @@ pub struct RenderActor { impl RenderActor { pub fn new( mailbox: broadcast::Receiver, + direct_mailbox: mpsc::UnboundedReceiver, view: Arc>>>, editor_conn_sender: mpsc::UnboundedSender, svg_sender: mpsc::UnboundedSender>, @@ -60,6 +62,7 @@ impl RenderActor { ) -> Self { let mut res = Self { mailbox, + direct_mailbox, view, renderer: IncrSvgDocServer::default(), editor_conn_sender, @@ -70,10 +73,11 @@ impl RenderActor { res } - async fn process_message(&mut self, msg: RenderActorRequest) -> bool { + async fn process_message(&mut self, msg: RenderActorRequest) -> (bool, bool) { log::trace!("RenderActor: received message: {msg:?}"); - let res = msg.is_full_render(); + let is_full_render = msg.is_full_render(); + let is_incremental_render = matches!(msg, RenderActorRequest::RenderIncremental); match msg { RenderActorRequest::EditorResolveSpanRange(span_range) => { log::debug!("RenderActor: resolving EditorResolveSpanRange: {span_range:?}"); @@ -86,7 +90,7 @@ impl RenderActor { Ok(spans) => spans, Err(err) => { log::info!("RenderActor: failed to resolve span: {err}"); - return false; + return (false, false); } }; @@ -119,28 +123,50 @@ impl RenderActor { RenderActorRequest::RenderFullLatest | RenderActorRequest::RenderIncremental => {} } - res + (is_full_render, is_incremental_render) } pub async fn run(mut self) { loop { let mut has_full_render = false; + let mut has_incremental_render = false; log::debug!("RenderActor: waiting for message"); - match self.mailbox.recv().await { - Ok(msg) => { - has_full_render |= self.process_message(msg).await; + tokio::select! { + msg = self.mailbox.recv() => { + match msg { + Ok(msg) => { + let (full, incremental) = self.process_message(msg).await; + has_full_render |= full; + has_incremental_render |= incremental; + } + Err(broadcast::error::RecvError::Closed) => { + log::info!("RenderActor: no more messages"); + break; + } + Err(broadcast::error::RecvError::Lagged(_)) => { + log::info!("RenderActor: lagged message. Some events are dropped"); + } + } } - Err(broadcast::error::RecvError::Closed) => { - log::info!("RenderActor: no more messages"); - break; + Some(msg) = self.direct_mailbox.recv() => { + let (full, incremental) = self.process_message(msg).await; + has_full_render |= full; + has_incremental_render |= incremental; } - Err(broadcast::error::RecvError::Lagged(_)) => { - log::info!("RenderActor: lagged message. Some events are dropped"); + else => { + break; } } // read the queue to empty while let Ok(msg) = self.mailbox.try_recv() { - has_full_render |= self.process_message(msg).await; + let (full, incremental) = self.process_message(msg).await; + has_full_render |= full; + has_incremental_render |= incremental; + } + while let Ok(msg) = self.direct_mailbox.try_recv() { + let (full, incremental) = self.process_message(msg).await; + has_full_render |= full; + has_incremental_render |= incremental; } // if a full render is requested, we render the latest document // otherwise, we render the incremental changes for only once @@ -151,7 +177,7 @@ impl RenderActor { continue; }; - let data = self.render(has_full_render, &document); + let data = self.render(has_full_render, has_incremental_render, &document); let Ok(_) = self.svg_sender.send(data) else { log::info!("RenderActor: svg_sender is dropped"); break; @@ -160,13 +186,22 @@ impl RenderActor { log::info!("RenderActor: exiting") } - fn render(&mut self, has_full_render: bool, document: &TypstDocument) -> Vec { - if has_full_render { - if let Some(data) = self.render_full() { - data + fn render( + &mut self, + has_full_render: bool, + has_incremental_render: bool, + document: &TypstDocument, + ) -> Vec { + if has_incremental_render { + let delta = self.render_delta(document); + if has_full_render { + self.render_full().unwrap_or(delta) } else { - self.render_delta(document) + delta } + } else if has_full_render { + self.render_full() + .unwrap_or_else(|| self.render_delta(document)) } else { self.render_delta(document) } diff --git a/crates/typst-preview/src/actor/webview.rs b/crates/typst-preview/src/actor/webview.rs index f41753344..d80854ffc 100644 --- a/crates/typst-preview/src/actor/webview.rs +++ b/crates/typst-preview/src/actor/webview.rs @@ -43,7 +43,7 @@ pub struct WebviewActor<'a, C> { broadcast_sender: broadcast::Sender, editor_sender: mpsc::UnboundedSender, - render_sender: broadcast::Sender, + render_sender: mpsc::UnboundedSender, } pub struct Channels { @@ -69,7 +69,7 @@ where broadcast_sender: broadcast::Sender, mailbox: broadcast::Receiver, editor_sender: mpsc::UnboundedSender, - render_sender: broadcast::Sender, + render_sender: mpsc::UnboundedSender, ) -> Self { Self { webview_websocket_conn: websocket_conn, diff --git a/crates/typst-preview/src/lib.rs b/crates/typst-preview/src/lib.rs index 584998c75..1062c4fb1 100644 --- a/crates/typst-preview/src/lib.rs +++ b/crates/typst-preview/src/lib.rs @@ -176,16 +176,18 @@ impl Previewer { } let actor::webview::Channels { svg } = actor::webview::WebviewActor::<'_, C>::set_up_channels(); + let (direct_render_tx, direct_render_rx) = mpsc::unbounded_channel(); let webview_actor = actor::webview::WebviewActor::new( conn, svg.1, h.webview_tx.clone(), h.webview_tx.subscribe(), h.editor_tx.clone(), - h.renderer_tx.clone(), + direct_render_tx, ); let render_actor = actor::render::RenderActor::new( h.renderer_tx.subscribe(), + direct_render_rx, h.doc_sender.clone(), h.editor_tx.clone(), svg.0, diff --git a/tools/typst-preview-frontend/src/main.js b/tools/typst-preview-frontend/src/main.js index 35d8b448c..14d6d9896 100644 --- a/tools/typst-preview-frontend/src/main.js +++ b/tools/typst-preview-frontend/src/main.js @@ -43,7 +43,12 @@ function retrieveWsArgs() { /// the websocket connection requires an absolute url. /// /// See [WebSocket and relative URLs](https://github.com/whatwg/websockets/issues/20) - let urlObject = new URL("ws://127.0.0.1:23625", window.location.href); + let url = "ws://127.0.0.1:23625"; + if (!url) { + return { url: "", previewMode, isContentPreview: false }; + } + + let urlObject = new URL(url, window.location.href); /// Rewrite the protocol to websocket. urlObject.protocol = urlObject.protocol.replace("https:", "wss:").replace("http:", "ws:"); if (location.href.startsWith("https://")) {