diagram plugins

This commit is contained in:
Martin Kavík 2024-11-25 22:13:39 +01:00
parent 1cdb558823
commit 01a9501f86
29 changed files with 1781 additions and 267 deletions

View file

@ -76,5 +76,15 @@ mod js_bridge {
#[wasm_bindgen(method)]
pub fn draw_diagram_element(this: &ExcalidrawController, excalidraw_element: JsValue);
#[wasm_bindgen(method)]
pub fn listen_for_component_text_changes(
this: &ExcalidrawController,
component_id: &str,
on_change: &Closure<dyn Fn(String)>,
);
#[wasm_bindgen(method)]
pub fn set_component_text(this: &ExcalidrawController, component_id: &str, text: &str);
}
}

View file

@ -1,4 +1,5 @@
use std::sync::Arc;
use shared::DiagramConnectorMessage;
use std::{mem, sync::Arc};
use zoon::*;
mod platform;
@ -53,11 +54,53 @@ static STORE: Lazy<Store> = lazy::default();
fn main() {
start_app("app", root);
Task::start(async {
// https://github.com/tauri-apps/tauri/issues/5170
Timer::sleep(100).await;
platform::show_window().await;
});
Task::start(async {
platform::listen_diagram_connectors_messages(|message| {
match message {
DiagramConnectorMessage::ListenForComponentTextChanges {
diagram_connector_name,
component_id,
} => {
let closure = Closure::new({
// @TODO Rcs/Arcs?
let diagram_connector_name = diagram_connector_name.clone();
let component_id = component_id.clone();
move |text| {
Task::start(platform::notify_diagram_connector_text_change(
diagram_connector_name.clone(),
component_id.clone(),
text,
));
}
});
STORE
.excalidraw_canvas_controller
.lock_ref()
.lock_ref()
.as_ref()
.unwrap_throw()
.listen_for_component_text_changes(&component_id, &closure);
// @TODO don't forget
mem::forget(closure);
}
DiagramConnectorMessage::SetComponentText { component_id, text } => STORE
.excalidraw_canvas_controller
.lock_ref()
.lock_ref()
.as_ref()
.unwrap_throw()
.set_component_text(&component_id, &text),
}
})
.await
});
}
fn root() -> impl Element {

View file

@ -4,6 +4,7 @@
// NOTE: `FASTWAVE_PLATFORM` is set in `Makefile.toml` tasks and then in `build.rs`
use crate::STORE;
use shared::DiagramConnectorMessage;
#[cfg(FASTWAVE_PLATFORM = "TAURI")]
mod tauri;
@ -17,10 +18,17 @@ use browser as platform;
type Filename = String;
type JavascriptCode = String;
type AddedDecodersCount = usize;
type RemovedDecodersCount = usize;
type DecoderPath = String;
type AddedDiagramConnectorsCount = usize;
type RemovedDiagramConnectorsCount = usize;
type DiagramConnectorPath = String;
type DiagramConnectorName = String;
type ComponentId = String;
pub async fn show_window() {
platform::show_window().await
}
@ -85,3 +93,29 @@ async fn redraw_all_timeline_rows() {
controller.redraw_all_rows().await
}
}
pub async fn add_diagram_connectors(
diagram_connector_paths: Vec<DecoderPath>,
) -> AddedDecodersCount {
let count = platform::add_diagram_connectors(diagram_connector_paths).await;
count
}
pub async fn remove_all_diagram_connectors() -> RemovedDecodersCount {
let count = platform::remove_all_diagram_connectors().await;
count
}
pub async fn listen_diagram_connectors_messages(
on_message: impl FnMut(DiagramConnectorMessage) + 'static,
) {
platform::listen_diagram_connectors_messages(on_message).await;
}
pub async fn notify_diagram_connector_text_change(
diagram_connector: DiagramConnectorName,
component_id: ComponentId,
text: String,
) {
platform::notify_diagram_connector_text_change(diagram_connector, component_id, text).await;
}

View file

@ -136,3 +136,33 @@ pub(super) async fn remove_all_decoders() -> super::RemovedDecodersCount {
eprintln!("Removing decoders is not supported in the browser.");
0
}
pub(super) async fn add_diagram_connectors(
diagram_connector_paths: Vec<super::DecoderPath>,
) -> super::AddedDecodersCount {
// @TODO error message for user
eprintln!("Adding diagram connectors is not supported in the browser.");
0
}
pub(super) async fn remove_all_diagram_connectors() -> super::RemovedDiagramConnectorsCount {
// @TODO error message for user
eprintln!("Removing diagram connectors is not supported in the browser.");
0
}
pub async fn listen_diagram_connectors_messages(
on_message: impl FnMut(DiagramConnectorMessage) + 'static,
) {
// @TODO error message for user
eprintln!("Removing listen for diagram connectors messages is not supported in the browser.");
}
pub async fn notify_diagram_connector_text_change(
diagram_connector: DiagramConnectorName,
component_id: ComponentId,
text: String,
) {
// @TODO error message for user
eprintln!("Diagram connectors notifications are not supported in the browser.");
}

View file

@ -1,3 +1,4 @@
use shared::DiagramConnectorMessage;
use zoon::*;
pub(super) async fn show_window() {
@ -68,6 +69,44 @@ pub(super) async fn remove_all_decoders() -> super::RemovedDecodersCount {
.unwrap_throw()
}
pub(super) async fn add_diagram_connectors(
diagram_connector_paths: Vec<super::DecoderPath>,
) -> super::AddedDiagramConnectorsCount {
serde_wasm_bindgen::from_value(
tauri_glue::add_diagram_connectors(diagram_connector_paths)
.await
.unwrap_throw(),
)
.unwrap_throw()
}
pub(super) async fn remove_all_diagram_connectors() -> super::RemovedDiagramConnectorsCount {
serde_wasm_bindgen::from_value(
tauri_glue::remove_all_diagram_connectors()
.await
.unwrap_throw(),
)
.unwrap_throw()
}
pub(super) async fn listen_diagram_connectors_messages(
mut on_message: impl FnMut(DiagramConnectorMessage) + 'static,
) {
let on_message =
move |message: JsValue| on_message(serde_wasm_bindgen::from_value(message).unwrap_throw());
tauri_glue::listen_diagram_connectors_messages(Closure::new(on_message).into_js_value()).await
}
pub(super) async fn notify_diagram_connector_text_change(
diagram_connector: super::DiagramConnectorName,
component_id: super::ComponentId,
text: String,
) {
tauri_glue::notify_diagram_connector_text_change(diagram_connector, component_id, text)
.await
.unwrap_throw();
}
mod tauri_glue {
use zoon::*;
@ -106,5 +145,22 @@ mod tauri_glue {
#[wasm_bindgen(catch)]
pub async fn remove_all_decoders() -> Result<JsValue, JsValue>;
#[wasm_bindgen(catch)]
pub async fn add_diagram_connectors(
diagram_connector_paths: Vec<super::super::DiagramConnectorPath>,
) -> Result<JsValue, JsValue>;
#[wasm_bindgen(catch)]
pub async fn remove_all_diagram_connectors() -> Result<JsValue, JsValue>;
pub async fn listen_diagram_connectors_messages(on_event: JsValue);
#[wasm_bindgen(catch)]
pub async fn notify_diagram_connector_text_change(
diagram_connector: super::super::DiagramConnectorName,
component_id: super::super::ComponentId,
text: String,
) -> Result<(), JsValue>;
}
}

View file

@ -3,10 +3,15 @@ use wellen::GetItem;
use zoon::*;
type FullVarName = String;
type AddedDecodersCount = usize;
type RemovedDecodersCount = usize;
type DecoderPath = String;
type AddedDiagramConnectorsCount = usize;
type RemovedDiagramConnectorsCount = usize;
type DiagramConnectorPath = String;
#[wasm_bindgen(module = "/typescript/bundles/strict_eval.js")]
extern "C" {
#[wasm_bindgen(catch)]
@ -91,4 +96,16 @@ impl FW {
controller.draw_diagram_element(excalidraw_element)
}
}
/// JS: `FW.add_diagram_connectors(["../test_files/components/rust_diagram_connector/rust_diagram_connector.wasm"])` -> `1`
pub async fn add_diagram_connectors(
connector_paths: Vec<DiagramConnectorPath>,
) -> AddedDiagramConnectorsCount {
platform::add_diagram_connectors(connector_paths).await
}
/// JS: `FW.remove_all_diagram_connectors()` -> `5`
pub async fn remove_all_diagram_connectors() -> RemovedDiagramConnectorsCount {
platform::remove_all_diagram_connectors().await
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -7,17 +7,64 @@ import * as React from 'react'
import * as ReactDOM from 'react-dom/client'
export class ExcalidrawController {
api: ExcalidrawImperativeAPI | undefined
api: Promise<ExcalidrawImperativeAPI>
resolve_api: (api: ExcalidrawImperativeAPI) => void
constructor() {}
constructor() {
this.resolve_api = (api) => {};
this.api = new Promise(resolve => {
this.resolve_api = (api) => resolve(api)
});
}
draw_diagram_element(excalidraw_element: ExcalidrawElement) {
if (typeof this.api !== 'undefined') {
const elements = this.api.getSceneElements()
this.api.updateScene({
this.api.then(api => {
const elements = api.getSceneElements()
api.updateScene({
elements: elements.concat(excalidraw_element)
})
}
});
}
listen_for_component_text_changes(id: string, on_change: (text: string) => void) {
this.api.then(api => {
let old_text: string | null = null;
api.onChange((elements: readonly ExcalidrawElement[]) => {
const element = elements.find(element => element.id === id);
if (typeof element !== 'undefined') {
if (element.type === 'text') {
if (old_text === null) {
old_text = element.text;
on_change(old_text);
} else {
if (old_text !== element.text) {
old_text = element.text;
on_change(old_text);
}
}
}
}
})
})
}
set_component_text(id: string, text: string) {
this.api.then(api => {
let element_found = false;
const elements = api.getSceneElements().map(element => {
if (element.id === id) {
element_found = true;
return { ...element, text: text, originalText: text }
} else {
return element
}
});
if (element_found) {
api.updateScene({
elements
})
}
})
}
async init(parent_element: HTMLElement) {
@ -37,7 +84,7 @@ export class ExcalidrawController {
// Font family: Code
currentItemFontFamily: 3,
}}}
excalidrawAPI={(api) => this.api = api}
excalidrawAPI={(api) => this.resolve_api(api)}
>
<MainMenu>
<MainMenu.DefaultItems.LoadScene />

View file

@ -1,18 +1,26 @@
// @TODO use TS and Tauri bindgens to make this code properly typed
import { core } from '@tauri-apps/api'
import { core, event } from '@tauri-apps/api'
const invoke = core.invoke;
const listen = event.listen;
type Filename = string;
type JavascriptCode = string;
type WellenHierarchy = unknown;
type Timeline = unknown;
type VarFormat = unknown;
type AddedDecodersCount = number;
type RemovedDecodersCount = number;
type DecoderPath = string;
type AddedDiagramConnectorsCount = number;
type RemovedDiagramConnectorsCount = number;
type DiagramConnectorPath = string;
type DiagramConnectorName = string;
type ComponentId = string;
export async function show_window(): Promise<void> {
return await invoke("show_window");
}
@ -58,3 +66,19 @@ export async function add_decoders(decoder_paths: Array<DecoderPath>): Promise<A
export async function remove_all_decoders(): Promise<RemovedDecodersCount> {
return await invoke("remove_all_decoders");
}
export async function add_diagram_connectors(diagram_connector_paths: Array<DiagramConnectorPath>): Promise<AddedDiagramConnectorsCount> {
return await invoke("add_diagram_connectors", { diagram_connector_paths });
}
export async function remove_all_diagram_connectors(): Promise<RemovedDiagramConnectorsCount> {
return await invoke("remove_all_diagram_connectors");
}
export async function listen_diagram_connectors_messages(on_message: (message: any) => void) {
return await listen("diagram_connector_message", (message) => on_message(message.payload));
}
export async function notify_diagram_connector_text_change(diagram_connector: DiagramConnectorName, component_id: ComponentId, text: string): Promise<void> {
return await invoke("notify_diagram_connector_text_change", { diagram_connector, component_id, text });
}