added an indicator for compiling

This commit is contained in:
Parker TenBroeck 2026-03-14 10:38:37 -04:00
parent 9ecb9f8f87
commit 9756df6701
5 changed files with 108 additions and 5 deletions

View file

@ -184,6 +184,7 @@ pub enum ClientMsg {
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
pub enum ServerMsg<'a> { pub enum ServerMsg<'a> {
Log { stream: &'a str, line: &'a str }, Log { stream: &'a str, line: &'a str },
Compiling,
Start, Start,
Stop, Stop,
Led(u32), Led(u32),

View file

@ -19,9 +19,24 @@ pub async fn ws_handler(socket: WebSocket, refresh_time: Duration) {
return; return;
}; };
_ = sender
.send(Message::Text(
serde_json::to_string(&ServerMsg::Compiling)
.unwrap_or_default()
.into(),
))
.await;
let temp_build = match build::copy_and_build(files).await { let temp_build = match build::copy_and_build(files).await {
Ok(build) => build, Ok(build) => build,
Err(err) => { Err(err) => {
_ = sender
.send(Message::Text(
serde_json::to_string(&ServerMsg::Stop)
.unwrap_or_default()
.into(),
))
.await;
_ = sender _ = sender
.send(Message::Text(format!("Failed to build: {err}").into())) .send(Message::Text(format!("Failed to build: {err}").into()))
.await; .await;
@ -35,12 +50,26 @@ pub async fn ws_handler(socket: WebSocket, refresh_time: Duration) {
let mut process = match run::run(&artifact_dir, &artifact).await { let mut process = match run::run(&artifact_dir, &artifact).await {
Ok(process) => process, Ok(process) => process,
Err(err) => { Err(err) => {
_ = sender
.send(Message::Text(
serde_json::to_string(&ServerMsg::Stop)
.unwrap_or_default()
.into(),
))
.await;
_ = sender _ = sender
.send(Message::Text(format!("Failed to run: {err}").into())) .send(Message::Text(format!("Failed to run: {err}").into()))
.await; .await;
return; return;
} }
}; };
_ = sender
.send(Message::Text(
serde_json::to_string(&ServerMsg::Start)
.unwrap_or_default()
.into(),
))
.await;
let mut sout = BufReader::new(process.stdout).lines(); let mut sout = BufReader::new(process.stdout).lines();
let mut serr = BufReader::new(process.stderr).lines(); let mut serr = BufReader::new(process.stderr).lines();
@ -80,7 +109,16 @@ pub async fn ws_handler(socket: WebSocket, refresh_time: Duration) {
}; };
sender.send(Message::Text(serde_json::to_string(&msg)?.into())).await?; sender.send(Message::Text(serde_json::to_string(&msg)?.into())).await?;
}, },
Ok(None) => break, Ok(None) => {
_ = sender
.send(Message::Text(
serde_json::to_string(&ServerMsg::Stop)
.unwrap_or_default()
.into(),
))
.await;
break;
},
Err(err) => { Err(err) => {
Err(format!("Failed to read proccess sout: {err}"))?; Err(format!("Failed to read proccess sout: {err}"))?;
} }
@ -105,7 +143,16 @@ pub async fn ws_handler(socket: WebSocket, refresh_time: Duration) {
}; };
sender.send(Message::Text(serde_json::to_string(&msg)?.into())).await?; sender.send(Message::Text(serde_json::to_string(&msg)?.into())).await?;
}, },
Ok(None) => break, Ok(None) => {
_ = sender
.send(Message::Text(
serde_json::to_string(&ServerMsg::Stop)
.unwrap_or_default()
.into(),
))
.await;
break;
},
Err(err) => { Err(err) => {
Err(format!("Failed to read proccess serr: {err}"))? Err(format!("Failed to read proccess serr: {err}"))?
} }

View file

@ -197,9 +197,26 @@ impl Handler {
} }
async fn run_program(&mut self) { async fn run_program(&mut self) {
_ = self
.sender
.send(Message::Text(
serde_json::to_string(&ServerMsg::Compiling)
.unwrap_or_default()
.into(),
))
.await;
let artifact = match build::build(&self.build_dir, &self.src_dir).await { let artifact = match build::build(&self.build_dir, &self.src_dir).await {
Ok(artifact) => artifact, Ok(artifact) => artifact,
Err(err) => { Err(err) => {
_ = self
.sender
.send(Message::Text(
serde_json::to_string(&ServerMsg::Stop)
.unwrap_or_default()
.into(),
))
.await;
_ = self.eprint(format!("Failed to build: {err}")).await; _ = self.eprint(format!("Failed to build: {err}")).await;
return; return;
} }
@ -208,6 +225,14 @@ impl Handler {
let process = match run::run(&self.build_dir, &artifact).await { let process = match run::run(&self.build_dir, &artifact).await {
Ok(process) => process, Ok(process) => process,
Err(err) => { Err(err) => {
_ = self
.sender
.send(Message::Text(
serde_json::to_string(&ServerMsg::Stop)
.unwrap_or_default()
.into(),
))
.await;
self.eprint(format!("Failed to run: {err}")).await; self.eprint(format!("Failed to run: {err}")).await;
return; return;
} }

View file

@ -693,6 +693,7 @@ class CircuitUiApp {
this.config = config; this.config = config;
this.dom = getDomRefs(); this.dom = getDomRefs();
this.mode = config.initialMode; this.mode = config.initialMode;
this.isCompiling = false;
this.isRunning = false; this.isRunning = false;
this.reconnectTimer = null; this.reconnectTimer = null;
@ -725,19 +726,26 @@ class CircuitUiApp {
if (this.mode === "uploaded") { if (this.mode === "uploaded") {
this.connection.send(this.editor.getFilesPayload()); this.connection.send(this.editor.getFilesPayload());
} }
this.connection.send({ input: this.inputs.getInputPayload() });
this.setRunButtonEnabled(true); this.setRunButtonEnabled(true);
this.updateStatusIndicator(); this.updateStatusIndicator();
}, },
onMessage: (parsed, raw) => { onMessage: (parsed, raw) => {
if (isUnitMessage(parsed, "compiling")) {
this.setCompiling(true);
return;
}
if (isUnitMessage(parsed, "start")) { if (isUnitMessage(parsed, "start")) {
this.logs.clear(); this.logs.clear();
this.outputs.resetVisuals(); this.outputs.resetVisuals();
this.setCompiling(false);
this.setRunning(true); this.setRunning(true);
this.connection.send({ input: this.inputs.getInputPayload() });
return; return;
} }
if (isUnitMessage(parsed, "stop")) { if (isUnitMessage(parsed, "stop")) {
this.setCompiling(false);
this.setRunning(false); this.setRunning(false);
return; return;
} }
@ -755,6 +763,7 @@ class CircuitUiApp {
}, },
onClose: () => { onClose: () => {
this.setRunButtonEnabled(false); this.setRunButtonEnabled(false);
this.setCompiling(false);
this.setRunning(false); this.setRunning(false);
this.updateStatusIndicator(); this.updateStatusIndicator();
if (this.mode === "workspace") { if (this.mode === "workspace") {
@ -825,6 +834,14 @@ class CircuitUiApp {
this.cancelReconnect(); this.cancelReconnect();
} }
this.setCompiling(false);
this.setRunning(false);
this.updateStatusIndicator();
}
setCompiling(compiling) {
this.isCompiling = Boolean(compiling);
this.dom.runToggleBtn.disabled = this.isCompiling || !this.connection.isConnected();
this.updateStatusIndicator(); this.updateStatusIndicator();
} }
@ -836,7 +853,7 @@ class CircuitUiApp {
} }
setRunButtonEnabled(enabled) { setRunButtonEnabled(enabled) {
this.dom.runToggleBtn.disabled = !enabled; this.dom.runToggleBtn.disabled = !enabled || this.isCompiling;
this.updateStatusIndicator(); this.updateStatusIndicator();
} }
@ -860,9 +877,10 @@ class CircuitUiApp {
updateStatusIndicator() { updateStatusIndicator() {
const pill = this.dom.statusPill; const pill = this.dom.statusPill;
const connected = this.connection.isConnected(); const connected = this.connection.isConnected();
const compiling = connected && this.isCompiling;
const running = connected && this.isRunning; const running = connected && this.isRunning;
pill.classList.remove("state-disabled", "state-connected", "state-running"); pill.classList.remove("state-disabled", "state-connected", "state-compiling", "state-running");
if (!connected) { if (!connected) {
pill.textContent = "DISABLED"; pill.textContent = "DISABLED";
@ -870,6 +888,12 @@ class CircuitUiApp {
return; return;
} }
if (compiling) {
pill.textContent = "COMPILING";
pill.classList.add("state-compiling");
return;
}
if (running) { if (running) {
pill.textContent = "RUNNING"; pill.textContent = "RUNNING";
pill.classList.add("state-running"); pill.classList.add("state-running");

View file

@ -213,6 +213,12 @@ textarea {
background: color-mix(in srgb, var(--accent-muted) 72%, black 28%); background: color-mix(in srgb, var(--accent-muted) 72%, black 28%);
} }
.pill.state-compiling {
border-color: color-mix(in srgb, var(--warn) 70%, white 18%);
background: color-mix(in srgb, var(--warn) 24%, var(--surface-header) 76%);
box-shadow: 0 0 0 1px color-mix(in srgb, var(--warn) 32%, transparent);
}
.pill.state-running { .pill.state-running {
border-color: var(--pill-connected-border); border-color: var(--pill-connected-border);
background: var(--pill-connected-bg); background: var(--pill-connected-bg);