Skip to content

Commit 6558185

Browse files
committed
feat: update react ui to present logs of Run/Replay
Signed-off-by: Nick Mitchell <[email protected]>
1 parent 5f26fbb commit 6558185

File tree

8 files changed

+156
-18
lines changed

8 files changed

+156
-18
lines changed

pdl-live-react/package-lock.json

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

pdl-live-react/package.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"prod:mac": "npm run prod:mac:1 && npm run prod:mac:2",
1010
"dev": "vite",
1111
"build": "tsc && vite build",
12-
"build:app": "npm run prep_interpreter && npm run tauri build",
12+
"build:app": "npm run prep-interpreter && npm run tauri build",
1313
"lint": "eslint .",
1414
"format": "prettier --write 'tests/**/*.ts' 'src/**/*.{ts,tsx,css}' && (cd src-tauri && cargo fmt)",
1515
"tauri": "tauri",
@@ -19,12 +19,14 @@
1919
"test": "concurrently -n 'quality,playwright' 'npm run test:quality' 'npm run test:ui'",
2020
"pdl": "./src-tauri/target/debug/PDL",
2121
"view": "npm run pdl view",
22-
"prep_interpreter": "if [ ! -d .venv ]; then python3.12 -mvenv .venv; source .venv/bin/activate; pip install -e ..; fi",
23-
"start": "npm run prep_interpreter && npm run tauri dev"
22+
"prep-interpreter": "if [ ! -d .venv ]; then npm run reprep-interpreter; fi",
23+
"reprep-interpreter": "source .venv/bin/activate; pip install -e ..",
24+
"start": "npm run prep-interpreter && npm run tauri dev"
2425
},
2526
"dependencies": {
2627
"@patternfly/react-code-editor": "^6.1.0",
2728
"@patternfly/react-core": "^6.1.0",
29+
"@patternfly/react-log-viewer": "^6.1.0",
2830
"@patternfly/react-topology": "^6.1.0",
2931
"@tauri-apps/api": "^2",
3032
"@tauri-apps/plugin-cli": "^2.2.0",

pdl-live-react/src-tauri/src/cli/run.rs

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,17 @@ use tempfile::NamedTempFile;
77
use tauri::path::BaseDirectory;
88
use tauri::Manager;
99

10+
#[derive(Clone, serde::Serialize)]
11+
pub struct Payload {
12+
done: bool,
13+
message: String,
14+
}
15+
1016
#[cfg(desktop)]
11-
fn pip_install_if_needed(app_handle: tauri::AppHandle) -> Result<String, tauri::Error> {
17+
fn pip_install_if_needed(
18+
app_handle: tauri::AppHandle,
19+
reader: &Option<tauri::ipc::Channel<Payload>>,
20+
) -> Result<String, tauri::Error> {
1221
let cache_path = app_handle.path().cache_dir()?.join("pdl");
1322

1423
create_dir_all(&cache_path)?;
@@ -29,7 +38,18 @@ fn pip_install_if_needed(app_handle: tauri::AppHandle) -> Result<String, tauri::
2938
); */
3039

3140
if !venv_path.exists() {
32-
println!("Creating virtual environment...");
41+
match reader {
42+
Some(r) => {
43+
r.send(Payload {
44+
done: false,
45+
message: "Creating virtual environment...".to_string(),
46+
})
47+
.unwrap();
48+
}
49+
None => {
50+
println!("Creating virtual environment...");
51+
}
52+
}
3353
let venv_path_string = venv_path.into_os_string().into_string().unwrap();
3454
let output = Command::new("python3.12")
3555
.args(["-mvenv", venv_path_string.as_str()])
@@ -83,9 +103,10 @@ pub fn run_pdl_program(
83103
source_file_path: String,
84104
app_handle: tauri::AppHandle,
85105
trace: bool,
106+
reader: Option<tauri::ipc::Channel<Payload>>,
86107
) -> Result<Option<Vec<u8>>, tauri::Error> {
87108
println!("Running {:?}", source_file_path);
88-
let activate = pip_install_if_needed(app_handle)?;
109+
let activate = pip_install_if_needed(app_handle, &reader)?;
89110

90111
let (trace_file, trace_arg) = if trace {
91112
let f = NamedTempFile::new()?;
@@ -118,8 +139,27 @@ pub fn run_pdl_program(
118139

119140
// Stream output.
120141
let lines = BufReader::new(stdout).lines();
121-
for line in lines {
122-
println!("{}", line.unwrap());
142+
match reader {
143+
Some(r) => {
144+
for line in lines {
145+
r.send(Payload {
146+
done: false,
147+
message: line.unwrap(),
148+
})
149+
.unwrap();
150+
}
151+
152+
r.send(Payload {
153+
done: true,
154+
message: "".to_string(),
155+
})
156+
.unwrap();
157+
}
158+
None => {
159+
for line in lines {
160+
println!("{}", line.unwrap());
161+
}
162+
}
123163
}
124164

125165
if trace {

pdl-live-react/src-tauri/src/cli/setup.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ pub fn cli(app: &mut tauri::App) -> Result<(), tauri::Error> {
2626
source_file_path.clone(),
2727
app.handle().clone(),
2828
false,
29+
None,
2930
)?;
3031
exit(0)
3132
}

pdl-live-react/src-tauri/src/commands/replay.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,23 @@ use std::io::Write;
22
use tempfile::NamedTempFile;
33
//use tauri::ipc::Response;
44

5-
use crate::cli::run::run_pdl_program;
5+
use crate::cli::run::{run_pdl_program, Payload};
66

77
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
88
#[tauri::command]
9-
pub async fn replay(app_handle: tauri::AppHandle, trace: String) -> Result<String, String> {
9+
pub async fn replay(
10+
app_handle: tauri::AppHandle,
11+
trace: String,
12+
reader: tauri::ipc::Channel<Payload>,
13+
) -> Result<String, String> {
1014
let mut file = NamedTempFile::new().map_err(|e| e.to_string())?;
1115
file.write_all(trace.as_bytes())
1216
.map_err(|e| e.to_string())?;
1317

1418
match file.path().to_str() {
1519
Some(path) => {
16-
let data =
17-
run_pdl_program(path.to_string(), app_handle, true).map_err(|e| e.to_string())?;
20+
let data = run_pdl_program(path.to_string(), app_handle, true, Some(reader))
21+
.map_err(|e| e.to_string())?;
1822
match data {
1923
Some(bytes) => {
2024
let string = String::from_utf8(bytes).expect("Our bytes should be valid utf8");

pdl-live-react/src/view/masonry/MasonryCombo.tsx

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
1-
import { useEffect, useMemo, useState } from "react"
2-
import { BackToTop, PageSection } from "@patternfly/react-core"
1+
import { useCallback, useEffect, useMemo, useState } from "react"
2+
import { LogViewer } from "@patternfly/react-log-viewer"
3+
import {
4+
Button,
5+
BackToTop,
6+
Modal,
7+
ModalBody,
8+
ModalFooter,
9+
ModalHeader,
10+
PageSection,
11+
} from "@patternfly/react-core"
312

413
import Topology from "../memory/Topology"
514
import extractVariables from "../memory/model"
@@ -46,6 +55,13 @@ export default function MasonryCombo({ value, setValue }: Props) {
4655
useEffect(() => setAsUserSetting(as), [as])
4756
useEffect(() => setSMLUserSetting(sml), [sml])
4857

58+
const [modalContent, setModalContent] = useState<null | {
59+
header: string
60+
body: string
61+
done?: boolean
62+
}>(null)
63+
const closeModal = useCallback(() => setModalContent(null), [setModalContent])
64+
4965
const { base, masonry, numbering } = useMemo(
5066
() => computeModel(block),
5167
[block],
@@ -69,6 +85,7 @@ export default function MasonryCombo({ value, setValue }: Props) {
6985
setSML={setSML}
7086
block={block}
7187
setValue={setValue}
88+
setModalContent={setModalContent}
7289
/>
7390
</PageSection>
7491
<PageSection
@@ -91,6 +108,29 @@ export default function MasonryCombo({ value, setValue }: Props) {
91108
</PageSection>
92109

93110
<BackToTop scrollableSelector=".pdl-masonry-page-section" />
111+
112+
<Modal variant="medium" isOpen={!!modalContent} onClose={closeModal}>
113+
<ModalHeader title={modalContent?.header} />
114+
<ModalBody tabIndex={0}>
115+
<LogViewer
116+
hasLineNumbers={false}
117+
data={modalContent?.body}
118+
theme="dark"
119+
scrollToRow={Number.MAX_VALUE}
120+
/>
121+
</ModalBody>
122+
123+
<ModalFooter>
124+
<Button
125+
key="Close"
126+
variant="primary"
127+
onClick={closeModal}
128+
isDisabled={!modalContent?.done}
129+
>
130+
Close
131+
</Button>
132+
</ModalFooter>
133+
</Modal>
94134
</>
95135
)
96136
}

pdl-live-react/src/view/masonry/Toolbar.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ export type Props = {
1919

2020
sml: SML
2121
setSML(sml: SML): void
22+
23+
setModalContent: import("react").Dispatch<
24+
import("react").SetStateAction<{ header: string; body: string } | null>
25+
>
2226
}
2327

2428
export default function MasonryToolbar({
@@ -28,12 +32,17 @@ export default function MasonryToolbar({
2832
setSML,
2933
block,
3034
setValue,
35+
setModalContent,
3136
}: Props) {
3237
return (
3338
<Toolbar className="pdl-masonry-toolbar">
3439
<ToolbarContent>
3540
<ToolbarGroup variant="action-group-plain">
36-
<ToolbarReplayButton block={block} setValue={setValue} />
41+
<ToolbarReplayButton
42+
block={block}
43+
setValue={setValue}
44+
setModalContent={setModalContent}
45+
/>
3746
<ToolbarShowSourceButton />
3847
</ToolbarGroup>
3948
<ToolbarGroup align={alignEnd} variant="action-group">

pdl-live-react/src/view/masonry/ToolbarReplayButton.tsx

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,38 @@
1-
import { invoke } from "@tauri-apps/api/core"
1+
import { invoke, Channel } from "@tauri-apps/api/core"
22
import { Button, Tooltip } from "@patternfly/react-core"
33
import { useCallback, useState } from "react"
44

55
type Props = {
66
block: import("../../pdl_ast").PdlBlock
77
setValue(value: string): void
8+
setModalContent: import("react").Dispatch<
9+
import("react").SetStateAction<{ header: string; body: string } | null>
10+
>
811
}
912

10-
export default function ToolbarReplayButton({ block, setValue }: Props) {
13+
export default function ToolbarReplayButton({
14+
block,
15+
setValue,
16+
setModalContent,
17+
}: Props) {
1118
const [isReplaying, setIsReplaying] = useState(false)
1219

1320
const handleClickSource = useCallback(async () => {
1421
setIsReplaying(true)
22+
23+
const reader = new Channel<{ message: string; done: boolean }>()
24+
reader.onmessage = ({ message, done = false }) => {
25+
//console.log(`got event ${message}`)
26+
setModalContent((content) => ({
27+
done,
28+
header: "Running Program",
29+
body: !content ? message : content.body + "\n" + message,
30+
}))
31+
}
32+
1533
try {
1634
const newTrace = (await invoke("replay", {
35+
reader,
1736
trace: JSON.stringify(block),
1837
})) as string
1938
console.log("new trace", newTrace)
@@ -23,7 +42,7 @@ export default function ToolbarReplayButton({ block, setValue }: Props) {
2342
} finally {
2443
setIsReplaying(false)
2544
}
26-
}, [block, setValue, setIsReplaying])
45+
}, [block, setValue, setIsReplaying, setModalContent])
2746

2847
return (
2948
<Tooltip content="Re-run this program">

0 commit comments

Comments
 (0)