From 49835ca6ca65e569ea2915b2ef4f7ce1c4988cae Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Fri, 16 Aug 2024 17:00:08 +0200 Subject: [PATCH 1/5] fix: workspace specific default tags do not override step level custom tags --- backend/windmill-worker/src/worker_flow.rs | 2 +- frontend/src/lib/components/FlowJobResult.svelte | 6 ++++-- .../src/lib/components/FlowStatusViewerInner.svelte | 12 ++++++++++-- frontend/src/lib/components/graph/model.ts | 1 + 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/backend/windmill-worker/src/worker_flow.rs b/backend/windmill-worker/src/worker_flow.rs index a4b65a589c..c27400c485 100644 --- a/backend/windmill-worker/src/worker_flow.rs +++ b/backend/windmill-worker/src/worker_flow.rs @@ -2379,7 +2379,7 @@ async fn push_next_flow_job continue_on_same_worker, err, flow_job.visible_to_owner, - if flow_job.tag == "flow" { + if flow_job.tag == "flow" || flow_job.tag == format!("flow-{}", flow_job.workspace_id) { payload_tag.tag } else { Some(flow_job.tag.clone()) diff --git a/frontend/src/lib/components/FlowJobResult.svelte b/frontend/src/lib/components/FlowJobResult.svelte index 41209fad98..9cb8eb382a 100644 --- a/frontend/src/lib/components/FlowJobResult.svelte +++ b/frontend/src/lib/components/FlowJobResult.svelte @@ -10,6 +10,7 @@ import type { DurationStatus } from './graph' import type { Writable } from 'svelte/store' + export let waitingForExecutor: boolean = false export let result: any export let logs: string | undefined export let col: boolean = false @@ -17,6 +18,7 @@ export let loading: boolean export let filename: string | undefined = undefined export let jobId: string | undefined = undefined + export let tag: string | undefined = undefined export let workspaceId: string | undefined = undefined export let refreshLog: boolean = false export let durationStates: Writable> | undefined @@ -79,9 +81,9 @@ {/if}
-
- +
diff --git a/frontend/src/lib/components/FlowStatusViewerInner.svelte b/frontend/src/lib/components/FlowStatusViewerInner.svelte index c875fd1e30..69bbb52080 100644 --- a/frontend/src/lib/components/FlowStatusViewerInner.svelte +++ b/frontend/src/lib/components/FlowStatusViewerInner.svelte @@ -171,7 +171,7 @@ mod.type === 'WaitingForEvents' && $localModuleStates?.[innerModules?.[i - 1]?.id ?? '']?.type == 'Success' ) { - setModuleState(mod.id ?? '', { type: mod.type, args: job?.args }) + setModuleState(mod.id ?? '', { type: mod.type, args: job?.args, tag: job?.tag }) } else if ( mod.type === 'WaitingForExecutor' && $localModuleStates[mod.id ?? '']?.scheduled_for == undefined @@ -187,7 +187,8 @@ scheduled_for: job?.['scheduled_for'], job_id: job?.id, parent_module: mod['parent_module'], - args: job?.args + args: job?.args, + tag: job?.tag } setModuleState(mod.id ?? '', newState) }) @@ -399,6 +400,7 @@ job_id: job.id, logs: job.logs, args: job.args, + tag: job.tag, started_at, parent_module: mod['parent_module'] }, @@ -417,6 +419,7 @@ logs: job.logs, result: job['result'], job_id: job.id, + tag: job.tag, parent_module: mod['parent_module'], duration_ms: job['duration_ms'], started_at: started_at, @@ -648,6 +651,7 @@ {/if} + diff --git a/frontend/src/lib/components/graph/model.ts b/frontend/src/lib/components/graph/model.ts index fb268a5acc..939160bff5 100644 --- a/frontend/src/lib/components/graph/model.ts +++ b/frontend/src/lib/components/graph/model.ts @@ -50,6 +50,7 @@ export type GraphModuleState = { flow_jobs_results?: any branchChosen?: number result?: any + tag?: string scheduled_for?: Date job_id?: string parent_module?: string From 5a8fa1d72487ac2a29dca8833b8c92b8cac3726e Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Fri, 16 Aug 2024 17:28:10 +0200 Subject: [PATCH 2/5] fix: improve password field lifetime incorrectly recycled too early --- ...df567eca550db1ae638fcb554622b61a5f4fb6b6696f6283516a.json} | 4 ++-- backend/src/monitor.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename backend/.sqlx/{query-bac12eff366f6b76f97d1a44041e0c1b5fdcb2c4205c1c1c0026b71ce404f089.json => query-b0c2f470f7d2df567eca550db1ae638fcb554622b61a5f4fb6b6696f6283516a.json} (73%) diff --git a/backend/.sqlx/query-bac12eff366f6b76f97d1a44041e0c1b5fdcb2c4205c1c1c0026b71ce404f089.json b/backend/.sqlx/query-b0c2f470f7d2df567eca550db1ae638fcb554622b61a5f4fb6b6696f6283516a.json similarity index 73% rename from backend/.sqlx/query-bac12eff366f6b76f97d1a44041e0c1b5fdcb2c4205c1c1c0026b71ce404f089.json rename to backend/.sqlx/query-b0c2f470f7d2df567eca550db1ae638fcb554622b61a5f4fb6b6696f6283516a.json index a84b6b5ff0..86a8abb432 100644 --- a/backend/.sqlx/query-bac12eff366f6b76f97d1a44041e0c1b5fdcb2c4205c1c1c0026b71ce404f089.json +++ b/backend/.sqlx/query-b0c2f470f7d2df567eca550db1ae638fcb554622b61a5f4fb6b6696f6283516a.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "DELETE FROM variable WHERE expires_at IS NOT NULL AND expires_at > now() RETURNING path", + "query": "DELETE FROM variable WHERE expires_at IS NOT NULL AND expires_at < now() RETURNING path", "describe": { "columns": [ { @@ -16,5 +16,5 @@ false ] }, - "hash": "bac12eff366f6b76f97d1a44041e0c1b5fdcb2c4205c1c1c0026b71ce404f089" + "hash": "b0c2f470f7d2df567eca550db1ae638fcb554622b61a5f4fb6b6696f6283516a" } diff --git a/backend/src/monitor.rs b/backend/src/monitor.rs index 1f8eae150a..ad835c713b 100644 --- a/backend/src/monitor.rs +++ b/backend/src/monitor.rs @@ -407,7 +407,7 @@ pub async fn delete_expired_items(db: &DB) -> () { } let deleted_expired_variables = sqlx::query_scalar!( - "DELETE FROM variable WHERE expires_at IS NOT NULL AND expires_at > now() RETURNING path", + "DELETE FROM variable WHERE expires_at IS NOT NULL AND expires_at < now() RETURNING path", ) .fetch_all(db) .await; From 99f7828ebb5fddf799afb52af7214ba4119e57b9 Mon Sep 17 00:00:00 2001 From: HugoCasa Date: Fri, 16 Aug 2024 17:30:44 +0200 Subject: [PATCH 3/5] feat: cache common hub scripts in image (#4249) * feat: cache common hub scripts in image * Delete backend/src/hubPaths.json * fix: missing file * fix: dockerfile * fix: cache by path * fix * fix * precreate cache folder --- Dockerfile | 6 ++- backend/src/main.rs | 39 +++++++++++++++++-- backend/windmill-api/src/apps.rs | 2 +- backend/windmill-api/src/embeddings.rs | 8 ++-- backend/windmill-api/src/flows.rs | 2 +- backend/windmill-api/src/scripts.rs | 3 +- backend/windmill-common/src/scripts.rs | 6 +-- backend/windmill-common/src/utils.rs | 19 ++++++--- backend/windmill-queue/src/jobs.rs | 23 +---------- backend/windmill-worker/src/worker.rs | 8 ++-- docker/DockerfileSlim | 6 ++- .../components/ErrorOrRecoveryHandler.svelte | 5 ++- .../apps/editor/AppReportsDrawer.svelte | 7 ++-- frontend/src/lib/hub.ts | 13 +++++++ frontend/src/lib/hubPaths.json | 9 +++++ .../(logged)/workspace_settings/+page.svelte | 5 ++- 16 files changed, 107 insertions(+), 54 deletions(-) create mode 100644 frontend/src/lib/hubPaths.json diff --git a/Dockerfile b/Dockerfile index 7fd89ba6fa..c0f15162de 100644 --- a/Dockerfile +++ b/Dockerfile @@ -175,7 +175,11 @@ WORKDIR ${APP} RUN ln -s ${APP}/windmill /usr/local/bin/windmill -RUN windmill cache +COPY ./frontend/src/lib/hubPaths.json ${APP}/hubPaths.json + +RUN windmill cache ${APP}/hubPaths.json + +RUN rm ${APP}/hubPaths.json EXPOSE 8000 diff --git a/backend/src/main.rs b/backend/src/main.rs index c26695d7c5..ebc7f6dc3b 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -6,15 +6,20 @@ * LICENSE-AGPL for a copy of the license. */ +use anyhow::Context; use gethostname::gethostname; use git_version::git_version; use rand::Rng; use sqlx::{postgres::PgListener, Pool, Postgres}; use std::{ + collections::HashMap, net::{IpAddr, Ipv4Addr, SocketAddr}, time::Duration, }; -use tokio::fs::DirBuilder; +use tokio::{ + fs::{create_dir_all, DirBuilder, File}, + io::AsyncReadExt, +}; use windmill_api::HTTP_CLIENT; #[cfg(feature = "enterprise")] @@ -54,9 +59,10 @@ use windmill_common::METRICS_ADDR; use windmill_common::global_settings::OBJECT_STORE_CACHE_CONFIG_SETTING; use windmill_worker::{ - BUN_BUNDLE_CACHE_DIR, BUN_CACHE_DIR, BUN_DEPSTAR_CACHE_DIR, DENO_CACHE_DIR, - DENO_CACHE_DIR_DEPS, DENO_CACHE_DIR_NPM, GO_BIN_CACHE_DIR, GO_CACHE_DIR, HUB_CACHE_DIR, - LOCK_CACHE_DIR, PIP_CACHE_DIR, POWERSHELL_CACHE_DIR, TAR_PIP_CACHE_DIR, TMP_LOGS_DIR, + get_hub_script_content_and_requirements, BUN_BUNDLE_CACHE_DIR, BUN_CACHE_DIR, + BUN_DEPSTAR_CACHE_DIR, DENO_CACHE_DIR, DENO_CACHE_DIR_DEPS, DENO_CACHE_DIR_NPM, + GO_BIN_CACHE_DIR, GO_CACHE_DIR, HUB_CACHE_DIR, LOCK_CACHE_DIR, PIP_CACHE_DIR, + POWERSHELL_CACHE_DIR, TAR_PIP_CACHE_DIR, TMP_LOGS_DIR, }; use crate::monitor::{ @@ -111,6 +117,28 @@ pub fn main() -> anyhow::Result<()> { create_and_run_current_thread_inner(windmill_main()) } +async fn cache_hub_scripts(file_path: Option) -> anyhow::Result<()> { + let file_path = file_path.unwrap_or("./hubPaths.json".to_string()); + let mut file = File::open(&file_path) + .await + .with_context(|| format!("Could not open {}, make sure it exists", &file_path))?; + let mut contents = String::new(); + file.read_to_string(&mut contents).await?; + let paths = serde_json::from_str::>(&contents).with_context(|| { + format!( + "Could not parse {}, make sure it is a valid JSON object with string keys and values", + &file_path + ) + })?; + + create_dir_all(HUB_CACHE_DIR).await?; + + for path in paths.values() { + get_hub_script_content_and_requirements(Some(path.to_string()), None).await?; + } + Ok(()) +} + async fn windmill_main() -> anyhow::Result<()> { dotenv::dotenv().ok(); @@ -141,6 +169,9 @@ async fn windmill_main() -> anyhow::Result<()> { { tracing::warn!("Embeddings are not enabled, ignoring..."); } + + cache_hub_scripts(std::env::args().nth(2)).await?; + return Ok(()); } "-v" | "--version" | "version" => { diff --git a/backend/windmill-api/src/apps.rs b/backend/windmill-api/src/apps.rs index 97409f5b81..9bfb84e34f 100644 --- a/backend/windmill-api/src/apps.rs +++ b/backend/windmill-api/src/apps.rs @@ -705,7 +705,7 @@ pub async fn get_hub_app_by_id( &format!("{}/apps/{}/json", *HUB_BASE_URL.read().await, id), false, None, - &db, + Some(&db), ) .await? .json() diff --git a/backend/windmill-api/src/embeddings.rs b/backend/windmill-api/src/embeddings.rs index 6848ceb3d3..5cc7226753 100644 --- a/backend/windmill-api/src/embeddings.rs +++ b/backend/windmill-api/src/embeddings.rs @@ -313,7 +313,7 @@ impl EmbeddingsDb { &format!("{}/scripts/embeddings", hub_base_url), false, None, - pg_db, + Some(pg_db), ) .await? } else { @@ -326,7 +326,7 @@ impl EmbeddingsDb { &format!("{}/scripts/embeddings", hub_base_url), false, None, - pg_db, + Some(pg_db), ) .await? } @@ -375,7 +375,7 @@ impl EmbeddingsDb { &format!("{}/resource_types/embeddings", hub_base_url), false, None, - pg_db, + Some(pg_db), ) .await? } else { @@ -388,7 +388,7 @@ impl EmbeddingsDb { &format!("{}/resource_types/embeddings", hub_base_url), false, None, - pg_db, + Some(pg_db), ) .await? } diff --git a/backend/windmill-api/src/flows.rs b/backend/windmill-api/src/flows.rs index c1f8ec70a4..ccec53c4dc 100644 --- a/backend/windmill-api/src/flows.rs +++ b/backend/windmill-api/src/flows.rs @@ -230,7 +230,7 @@ pub async fn get_hub_flow_by_id( &format!("{}/flows/{}/json", *HUB_BASE_URL.read().await, id), false, None, - &db, + Some(&db), ) .await? .json() diff --git a/backend/windmill-api/src/scripts.rs b/backend/windmill-api/src/scripts.rs index 0ed84404a8..41214dfcbb 100644 --- a/backend/windmill-api/src/scripts.rs +++ b/backend/windmill-api/src/scripts.rs @@ -817,7 +817,8 @@ pub async fn get_full_hub_script_by_path( Extension(db): Extension, ) -> JsonResult { Ok(Json( - windmill_common::scripts::get_full_hub_script_by_path(path, &HTTP_CLIENT, &db).await?, + windmill_common::scripts::get_full_hub_script_by_path(path, &HTTP_CLIENT, Some(&db)) + .await?, )) } diff --git a/backend/windmill-common/src/scripts.rs b/backend/windmill-common/src/scripts.rs index 86c506f467..b10558e40f 100644 --- a/backend/windmill-common/src/scripts.rs +++ b/backend/windmill-common/src/scripts.rs @@ -384,7 +384,7 @@ pub async fn get_hub_script_by_path( &format!("{}/raw/{}.ts", hub_base_url, path), true, None, - db, + Some(db), ) .await? .text() @@ -409,7 +409,7 @@ pub async fn get_hub_script_by_path( &format!("{}/raw/{}.ts", DEFAULT_HUB_BASE_URL, path), true, None, - db, + Some(db), ) .await? .text() @@ -427,7 +427,7 @@ pub async fn get_hub_script_by_path( pub async fn get_full_hub_script_by_path( path: StripPath, http_client: &reqwest::Client, - db: &DB, + db: Option<&DB>, ) -> crate::error::Result { let path = path .to_path() diff --git a/backend/windmill-common/src/utils.rs b/backend/windmill-common/src/utils.rs index 81031a7359..63cece0fe2 100644 --- a/backend/windmill-common/src/utils.rs +++ b/backend/windmill-common/src/utils.rs @@ -96,7 +96,7 @@ pub async fn query_elems_from_hub( reqwest::header::HeaderMap, axum::body::Body, )> { - let response = http_get_from_hub(http_client, url, false, query_params, db).await?; + let response = http_get_from_hub(http_client, url, false, query_params, Some(db)).await?; let status = response.status(); @@ -112,9 +112,18 @@ pub async fn http_get_from_hub( url: &str, plain: bool, query_params: Option>, - db: &Pool, + db: Option<&Pool>, ) -> Result { - let uid = get_uid(db).await; + let uid = match db { + Some(db) => match get_uid(db).await { + Ok(uid) => Some(uid), + Err(err) => { + tracing::info!("No valid uid found: {}", err); + None + } + }, + None => None, + }; let mut request = http_client.get(url).header( "Accept", @@ -125,10 +134,8 @@ pub async fn http_get_from_hub( }, ); - if let Ok(uid) = uid { + if let Some(uid) = uid { request = request.header("X-uid", uid); - } else { - tracing::info!("No valid uid found: {}", uid.err().unwrap()) } if let Some(query_params) = query_params { diff --git a/backend/windmill-queue/src/jobs.rs b/backend/windmill-queue/src/jobs.rs index 4214398752..7e1bf9b192 100644 --- a/backend/windmill-queue/src/jobs.rs +++ b/backend/windmill-queue/src/jobs.rs @@ -53,7 +53,6 @@ use windmill_common::{ jobs::{ get_payload_tag_from_prefixed_path, CompletedJob, JobKind, JobPayload, QueuedJob, RawCode, }, - oauth2::WORKSPACE_SLACK_BOT_TOKEN_PATH, schedule::Schedule, scripts::{get_full_hub_script_by_path, ScriptHash, ScriptLang}, users::{SUPERADMIN_NOTIFICATION_EMAIL, SUPERADMIN_SECRET_EMAIL}, @@ -1479,16 +1478,6 @@ pub async fn push_error_handler< } } - // TODO(gbouv): REMOVE THIS after December 1st 2023 and ping users to re-save their error handlers - if on_failure_path - .to_string() - .eq("script/hub/5792/workspace-or-schedule-error-handler-slack") - { - // default slack error handler being used -> we need to inject the slack token - let slack_resource = format!("$res:{WORKSPACE_SLACK_BOT_TOKEN_PATH}"); - extra.insert("slack".to_string(), to_raw_value(&slack_resource)); - } - let result = sanitize_result(result); let tx = PushIsolationLevel::IsolatedRoot(db.clone(), rsmq); @@ -1602,15 +1591,6 @@ async fn handle_recovered_schedule< )); } } - // TODO(gbouv): REMOVE THIS after December 1st 2023 and ping users to re-save their error handlers - if on_recovery_path - .to_string() - .eq("script/hub/2430/slack/schedule-recovery-handler-slack") - { - // default slack error handler being used -> we need to inject the slack token - let slack_resource = format!("$res:{WORKSPACE_SLACK_BOT_TOKEN_PATH}"); - extra.insert("slack".to_string(), to_raw_value(&slack_resource)); - } let args = error_job .result @@ -3137,7 +3117,8 @@ pub async fn push<'c, 'd, R: rsmq_async::RsmqConnection + Send + 'c>( } let hub_script = - get_full_hub_script_by_path(StripPath(path.clone()), &HTTP_CLIENT, _db).await?; + get_full_hub_script_by_path(StripPath(path.clone()), &HTTP_CLIENT, Some(_db)) + .await?; ( None, diff --git a/backend/windmill-worker/src/worker.rs b/backend/windmill-worker/src/worker.rs index b34fede376..6a6ccc7daf 100644 --- a/backend/windmill-worker/src/worker.rs +++ b/backend/windmill-worker/src/worker.rs @@ -2736,9 +2736,9 @@ pub struct ContentReqLangEnvs { pub codebase: Option, } -async fn get_hub_script_content_and_requirements( +pub async fn get_hub_script_content_and_requirements( script_path: Option, - db: &DB, + db: Option<&DB>, ) -> error::Result { let script_path = script_path .clone() @@ -2783,7 +2783,7 @@ async fn get_script_content_by_path( .clone() .ok_or_else(|| Error::InternalErr(format!("expected script path")))?; return if script_path.starts_with("hub/") { - get_hub_script_content_and_requirements(Some(script_path), db).await + get_hub_script_content_and_requirements(Some(script_path), Some(db)).await } else { let (script_hash, ..) = get_latest_deployed_hash_for_path(db, w_id, script_path.as_str()).await?; @@ -2870,7 +2870,7 @@ async fn handle_code_execution_job( codebase }}, JobKind::Script_Hub => { - get_hub_script_content_and_requirements(job.script_path.clone(), db).await? + get_hub_script_content_and_requirements(job.script_path.clone(), Some(db)).await? } JobKind::Script => { get_script_content_by_hash( diff --git a/docker/DockerfileSlim b/docker/DockerfileSlim index af27740cd6..47d26b0470 100644 --- a/docker/DockerfileSlim +++ b/docker/DockerfileSlim @@ -27,7 +27,11 @@ COPY --from=denoland/deno:1.44.4 --chmod=755 /usr/bin/deno /usr/bin/deno RUN ln -s ${APP}/windmill /usr/local/bin/windmill -RUN windmill cache +COPY ./frontend/src/lib/hubPaths.json ${APP}/hubPaths.json + +RUN windmill cache ${APP}/hubPaths.json + +RUN rm ${APP}/hubPaths.json EXPOSE 8000 diff --git a/frontend/src/lib/components/ErrorOrRecoveryHandler.svelte b/frontend/src/lib/components/ErrorOrRecoveryHandler.svelte index 465805fb61..c54965d8be 100644 --- a/frontend/src/lib/components/ErrorOrRecoveryHandler.svelte +++ b/frontend/src/lib/components/ErrorOrRecoveryHandler.svelte @@ -19,9 +19,10 @@ import { hubBaseUrlStore } from '$lib/stores' import { CheckCircle2, Loader2, RotateCw, XCircle } from 'lucide-svelte' + import { hubPaths } from '$lib/hub' - const slackRecoveryHandler = 'hub/2430/slack/schedule-recovery-handler-slack' - const slackHandlerScriptPath = 'hub/6512/workspace-or-schedule-error-handler-slack' + const slackRecoveryHandler = hubPaths.slackRecoveryHandler + const slackHandlerScriptPath = hubPaths.slackErrorHandler export let errorOrRecovery: 'error' | 'recovery' export let isEditable: boolean diff --git a/frontend/src/lib/components/apps/editor/AppReportsDrawer.svelte b/frontend/src/lib/components/apps/editor/AppReportsDrawer.svelte index c2c160722e..5df6e41fb2 100644 --- a/frontend/src/lib/components/apps/editor/AppReportsDrawer.svelte +++ b/frontend/src/lib/components/apps/editor/AppReportsDrawer.svelte @@ -24,6 +24,7 @@ import { RotateCw, Save } from 'lucide-svelte' import { CUSTOM_TAGS_SETTING, WORKSPACE_SLACK_BOT_TOKEN_PATH } from '$lib/consts' import { loadSchemaFromPath } from '$lib/infer' + import { hubPaths } from '$lib/hub' export let appPath: string export let open = false @@ -187,7 +188,7 @@ export async function main(app_path: string, startup_duration = 5, kind: 'pdf' | const notificationScripts = { discord: { - path: 'hub/7838/discord', + path: hubPaths.discordReport, schema: { type: 'object', properties: { @@ -203,7 +204,7 @@ export async function main(app_path: string, startup_duration = 5, kind: 'pdf' | } }, slack: { - path: 'hub/7836/slack', // if to be updated, also update it in in backend/windmill-queue/src/jobs.rs + path: hubPaths.slackReport, // if to be updated, also update it in in backend/windmill-queue/src/jobs.rs schema: { type: 'object', properties: { @@ -216,7 +217,7 @@ export async function main(app_path: string, startup_duration = 5, kind: 'pdf' | } }, email: { - path: 'hub/7837/smtp', + path: hubPaths.smtpReport, schema: { type: 'object', properties: { diff --git a/frontend/src/lib/hub.ts b/frontend/src/lib/hub.ts index cb4fba3f2a..f5ff47ff1a 100644 --- a/frontend/src/lib/hub.ts +++ b/frontend/src/lib/hub.ts @@ -1,6 +1,7 @@ import type { Schema } from './common' import { AppService, FlowService, type Flow, type Script } from './gen' import { encodeState } from './utils' +import rawHubPaths from './hubPaths.json?raw' export function scriptToHubUrl( content: string, @@ -55,3 +56,15 @@ export function appToHubUrl(staticApp: any, hubBaseUrl: string): URL { url.searchParams.append('app', encodeState(staticApp)) return url } + +type HubPaths = { + gitSync: string + gitSyncTest: string + slackErrorHandler: string + slackRecoveryHandler: string + slackReport: string + discordReport: string + smtpReport: string +} + +export const hubPaths = JSON.parse(rawHubPaths) as HubPaths diff --git a/frontend/src/lib/hubPaths.json b/frontend/src/lib/hubPaths.json new file mode 100644 index 0000000000..5d274614a0 --- /dev/null +++ b/frontend/src/lib/hubPaths.json @@ -0,0 +1,9 @@ +{ + "gitSync": "hub/8931/sync-script-to-git-repo-windmill", + "gitSyncTest": "hub/8944/git-repo-test-read-write-windmill", + "slackErrorHandler": "hub/6512/workspace-or-schedule-error-handler-slack", + "slackRecoveryHandler": "hub/2430/slack/schedule-recovery-handler-slack", + "slackReport": "hub/7836/slack", + "discordReport": "hub/7838/discord", + "smtpReport": "hub/7837/smtp" +} \ No newline at end of file diff --git a/frontend/src/routes/(root)/(logged)/workspace_settings/+page.svelte b/frontend/src/routes/(root)/(logged)/workspace_settings/+page.svelte index 723336e2a3..a06bed1d34 100644 --- a/frontend/src/routes/(root)/(logged)/workspace_settings/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/workspace_settings/+page.svelte @@ -55,6 +55,7 @@ type S3ResourceSettings } from '$lib/workspace_settings' import { base } from '$lib/base' + import { hubPaths } from '$lib/hub' type GitSyncTypeMap = { scripts: boolean @@ -138,7 +139,7 @@ | 'error_handler') ?? 'users' let usingOpenaiClientCredentialsOauth = false - const latestGitSyncHubScript = `hub/8931/sync-script-to-git-repo-windmill` + const latestGitSyncHubScript = hubPaths.gitSync // function getDropDownItems(username: string): DropdownItem[] { // return [ // { @@ -566,7 +567,7 @@ } let jobId = await JobService.runScriptByPath({ workspace: $workspaceStore!, - path: 'hub/8944/git-repo-test-read-write-windmill', + path: hubPaths.gitSyncTest, requestBody: { repo_url_resource_path: gitSyncRepository.git_repo_resource_path.replace('$res:', '') } From de4f5e301528dc8d34371d0d6b57622d51cecc92 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Fri, 16 Aug 2024 17:34:20 +0200 Subject: [PATCH 4/5] chore(main): release 1.381.0 (#4248) * chore(main): release 1.381.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel --- CHANGELOG.md | 18 ++++ backend/Cargo.lock | 100 +++++++++--------- backend/Cargo.toml | 4 +- backend/windmill-api/openapi.yaml | 2 +- benchmarks/lib.ts | 2 +- cli/main.ts | 2 +- frontend/package-lock.json | 4 +- frontend/package.json | 2 +- lsp/Pipfile | 4 +- openflow.openapi.yaml | 2 +- .../WindmillClient/WindmillClient.psd1 | 2 +- python-client/wmill/pyproject.toml | 2 +- python-client/wmill_pg/pyproject.toml | 2 +- typescript-client/jsr.json | 2 +- typescript-client/package.json | 2 +- version.txt | 2 +- 16 files changed, 85 insertions(+), 67 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6c41ad5e8..2b457783be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## [1.381.0](https://github.com/windmill-labs/windmill/compare/v1.380.0...v1.381.0) (2024-08-16) + + +### Features + +* add env to refresh cgroup readings ([#4250](https://github.com/windmill-labs/windmill/issues/4250)) ([e23c3fa](https://github.com/windmill-labs/windmill/commit/e23c3fad628cc718314472f3cb53c0f257c4c9e5)) +* cache common hub scripts in image ([#4249](https://github.com/windmill-labs/windmill/issues/4249)) ([99f7828](https://github.com/windmill-labs/windmill/commit/99f7828ebb5fddf799afb52af7214ba4119e57b9)) + + +### Bug Fixes + +* **cli:** add inject and define options ([dffd5f7](https://github.com/windmill-labs/windmill/commit/dffd5f7f7d5c84624953bf8c778b14586a14e2d4)) +* **frontend:** improve UI for email triggers ([#4243](https://github.com/windmill-labs/windmill/issues/4243)) ([6c9e32a](https://github.com/windmill-labs/windmill/commit/6c9e32af104a897e5c2a5b41867d2ffbbacc8acb)) +* improve password field lifetime incorrectly recycled too early ([5a8fa1d](https://github.com/windmill-labs/windmill/commit/5a8fa1d72487ac2a29dca8833b8c92b8cac3726e)) +* improve row update of aggrid table actions II ([3cf4f00](https://github.com/windmill-labs/windmill/commit/3cf4f00dca677fc592d5dde8a7b8fdcac5f08e0a)) +* **typescript-client:** add runFlow and runFlowAsync ([c9ef2c8](https://github.com/windmill-labs/windmill/commit/c9ef2c8e97bdecb16b9a54805f1c90523e3b406f)) +* workspace specific default tags do not override step level custom tags ([49835ca](https://github.com/windmill-labs/windmill/commit/49835ca6ca65e569ea2915b2ef4f7ce1c4988cae)) + ## [1.380.0](https://github.com/windmill-labs/windmill/compare/v1.379.4...v1.380.0) (2024-08-14) diff --git a/backend/Cargo.lock b/backend/Cargo.lock index a5f863c6ce..a553da04f0 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -845,9 +845,9 @@ dependencies = [ [[package]] name = "aws-sdk-sso" -version = "1.37.0" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1074e818fbe4f9169242d78448b15be8916a79daa38ea1231f2e2e10d993fcd2" +checksum = "fca5e0b9fb285638f1007e9d961d963b9e504ab968fe5a3807cce94070bd0ce3" dependencies = [ "aws-credential-types", "aws-runtime", @@ -867,9 +867,9 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.38.0" +version = "1.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29755c51e33fa3f678598f64324a169cf4b7d3c4865d2709d4308f53366a92a4" +checksum = "bc3e48ec239bb734db029ceef83599f4c9b3ce5d25c961b5bcd3f031c15bed54" dependencies = [ "aws-credential-types", "aws-runtime", @@ -889,9 +889,9 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.37.0" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e52dc3fd7dfa6c01a69cf3903e00aa467261639138a05b06cd92314d2c8fb07" +checksum = "ede095dfcc5c92b224813c24a82b65005a475c98d737e2726a898cf583e2e8bd" dependencies = [ "aws-credential-types", "aws-runtime", @@ -985,9 +985,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.6.2" +version = "1.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce87155eba55e11768b8c1afa607f3e864ae82f03caf63258b37455b0ad02537" +checksum = "0abbf454960d0db2ad12684a1640120e7557294b0ff8e2f11236290a1b293225" dependencies = [ "aws-smithy-async", "aws-smithy-http", @@ -1029,9 +1029,9 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "1.2.0" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfe321a6b21f5d8eabd0ade9c55d3d0335f3c3157fc2b3e87f05f34b539e4df5" +checksum = "6cee7cadb433c781d3299b916fbf620fea813bf38f49db282fb6858141a05cc8" dependencies = [ "base64-simd 0.8.0", "bytes", @@ -1483,18 +1483,18 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.16.3" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "102087e286b4677862ea56cf8fc58bb2cdfa8725c40ffb80fe3a008eb7f2fc83" +checksum = "6fd4c6dcc3b0aea2f5c0b4b82c2b15fe39ddbc76041a310848f4706edf76bb31" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.7.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ee891b04274a59bd38b412188e24b849617b2e45a0fd8d057deb63e7403761b" +checksum = "0cc8b54b395f2fcfbb3d90c47b01c7f444d94d05bdeb775811dec868ac3bbc26" dependencies = [ "proc-macro2", "quote", @@ -1613,9 +1613,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.11" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fb8dd288a69fc53a1996d7ecfbf4a20d59065bff137ce7e56bbd620de191189" +checksum = "72db2f7947ecee9b03b510377e8bb9077afa27176fdbff55c51027e976fdcc48" dependencies = [ "jobserver", "libc", @@ -1718,9 +1718,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.15" +version = "4.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d8838454fda655dafd3accb2b6e2bea645b9e4078abe84a22ceb947235c5cc" +checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019" dependencies = [ "clap_builder", "clap_derive", @@ -1758,9 +1758,9 @@ checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "cmake" -version = "0.1.50" +version = "0.1.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" +checksum = "fb1e43aa7fd152b1f968787f7dbcdeb306d1867ff373c69955211876c053f91a" dependencies = [ "cc", ] @@ -4660,9 +4660,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.155" +version = "0.2.156" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "a5f43f184355eefb8d17fc948dbecf6c13be3c141f20d834ae842193a448c72a" [[package]] name = "libgit2-sys" @@ -7466,9 +7466,9 @@ checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4" [[package]] name = "serde" -version = "1.0.207" +version = "1.0.208" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5665e14a49a4ea1b91029ba7d3bca9f299e1f7cfa194388ccc20f14743e784f2" +checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2" dependencies = [ "serde_derive", ] @@ -7507,9 +7507,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.207" +version = "1.0.208" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aea2634c86b0e8ef2cfdc0c340baede54ec27b1e46febd7f80dffb2aa44a00e" +checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" dependencies = [ "proc-macro2", "quote", @@ -7529,9 +7529,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.124" +version = "1.0.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66ad62847a56b3dba58cc891acd13884b9c61138d330c0d7b6181713d4fce38d" +checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed" dependencies = [ "indexmap 2.4.0", "itoa", @@ -7592,9 +7592,9 @@ dependencies = [ [[package]] name = "serde_tokenstream" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8790a7c3fe883e443eaa2af6f705952bc5d6e8671a220b9335c8cae92c037e74" +checksum = "64060d864397305347a78851c51588fd283767e7e7589829e8121d65512340f1" dependencies = [ "proc-macro2", "quote", @@ -10409,7 +10409,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windmill" -version = "1.380.0" +version = "1.381.0" dependencies = [ "anyhow", "axum", @@ -10449,7 +10449,7 @@ dependencies = [ [[package]] name = "windmill-api" -version = "1.380.0" +version = "1.381.0" dependencies = [ "anyhow", "argon2", @@ -10533,7 +10533,7 @@ dependencies = [ [[package]] name = "windmill-api-client" -version = "1.380.0" +version = "1.381.0" dependencies = [ "base64 0.21.7", "chrono", @@ -10551,7 +10551,7 @@ dependencies = [ [[package]] name = "windmill-audit" -version = "1.380.0" +version = "1.381.0" dependencies = [ "chrono", "serde", @@ -10564,7 +10564,7 @@ dependencies = [ [[package]] name = "windmill-common" -version = "1.380.0" +version = "1.381.0" dependencies = [ "anyhow", "async-stream", @@ -10605,7 +10605,7 @@ dependencies = [ [[package]] name = "windmill-git-sync" -version = "1.380.0" +version = "1.381.0" dependencies = [ "regex", "rsmq_async", @@ -10620,7 +10620,7 @@ dependencies = [ [[package]] name = "windmill-indexer" -version = "1.380.0" +version = "1.381.0" dependencies = [ "anyhow", "bytes", @@ -10641,7 +10641,7 @@ dependencies = [ [[package]] name = "windmill-parser" -version = "1.380.0" +version = "1.381.0" dependencies = [ "serde", "serde_json", @@ -10649,7 +10649,7 @@ dependencies = [ [[package]] name = "windmill-parser-bash" -version = "1.380.0" +version = "1.381.0" dependencies = [ "anyhow", "lazy_static", @@ -10660,7 +10660,7 @@ dependencies = [ [[package]] name = "windmill-parser-go" -version = "1.380.0" +version = "1.381.0" dependencies = [ "anyhow", "gosyn", @@ -10672,7 +10672,7 @@ dependencies = [ [[package]] name = "windmill-parser-graphql" -version = "1.380.0" +version = "1.381.0" dependencies = [ "anyhow", "lazy_static", @@ -10683,7 +10683,7 @@ dependencies = [ [[package]] name = "windmill-parser-php" -version = "1.380.0" +version = "1.381.0" dependencies = [ "anyhow", "convert_case 0.6.0", @@ -10697,7 +10697,7 @@ dependencies = [ [[package]] name = "windmill-parser-py" -version = "1.380.0" +version = "1.381.0" dependencies = [ "anyhow", "itertools 0.13.0", @@ -10708,7 +10708,7 @@ dependencies = [ [[package]] name = "windmill-parser-py-imports" -version = "1.380.0" +version = "1.381.0" dependencies = [ "anyhow", "async-recursion", @@ -10725,7 +10725,7 @@ dependencies = [ [[package]] name = "windmill-parser-sql" -version = "1.380.0" +version = "1.381.0" dependencies = [ "anyhow", "lazy_static", @@ -10736,7 +10736,7 @@ dependencies = [ [[package]] name = "windmill-parser-ts" -version = "1.380.0" +version = "1.381.0" dependencies = [ "anyhow", "convert_case 0.6.0", @@ -10755,7 +10755,7 @@ dependencies = [ [[package]] name = "windmill-parser-wasm" -version = "1.380.0" +version = "1.381.0" dependencies = [ "anyhow", "getrandom 0.2.15", @@ -10774,7 +10774,7 @@ dependencies = [ [[package]] name = "windmill-queue" -version = "1.380.0" +version = "1.381.0" dependencies = [ "anyhow", "async-recursion", @@ -10807,7 +10807,7 @@ dependencies = [ [[package]] name = "windmill-sql-datatype-parser-wasm" -version = "1.380.0" +version = "1.381.0" dependencies = [ "wasm-bindgen", "wasm-bindgen-test", @@ -10817,7 +10817,7 @@ dependencies = [ [[package]] name = "windmill-worker" -version = "1.380.0" +version = "1.381.0" dependencies = [ "anyhow", "async-recursion", diff --git a/backend/Cargo.toml b/backend/Cargo.toml index e033e5396d..906d9f08b9 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "windmill" -version = "1.380.0" +version = "1.381.0" authors.workspace = true edition.workspace = true @@ -25,7 +25,7 @@ members = [ ] [workspace.package] -version = "1.380.0" +version = "1.381.0" authors = ["Ruben Fiszel "] edition = "2021" diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 722f431336..0a44378b6d 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.3" info: - version: 1.380.0 + version: 1.381.0 title: Windmill API contact: diff --git a/benchmarks/lib.ts b/benchmarks/lib.ts index 018eb7a7d2..94d6034205 100644 --- a/benchmarks/lib.ts +++ b/benchmarks/lib.ts @@ -2,7 +2,7 @@ import { sleep } from "https://deno.land/x/sleep@v1.2.1/mod.ts"; import * as windmill from "https://deno.land/x/windmill@v1.174.0/mod.ts"; import * as api from "https://deno.land/x/windmill@v1.174.0/windmill-api/index.ts"; -export const VERSION = "v1.380.0"; +export const VERSION = "v1.381.0"; export async function login(email: string, password: string): Promise { return await windmill.UserService.login({ diff --git a/cli/main.ts b/cli/main.ts index dd9ee93d42..f2e1fd094b 100644 --- a/cli/main.ts +++ b/cli/main.ts @@ -34,7 +34,7 @@ addEventListener("error", (event) => { } }); -export const VERSION = "v1.380.0"; +export const VERSION = "v1.381.0"; let command: any = new Command() .name("wmill") diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 382f38ab4f..b4b09e7aa9 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "windmill-components", - "version": "1.380.0", + "version": "1.381.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "windmill-components", - "version": "1.380.0", + "version": "1.381.0", "license": "AGPL-3.0", "dependencies": { "@aws-crypto/sha256-js": "^4.0.0", diff --git a/frontend/package.json b/frontend/package.json index d0aaf6530e..2f5ce6b2fd 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "windmill-components", - "version": "1.380.0", + "version": "1.381.0", "scripts": { "dev": "vite dev", "build": "vite build", diff --git a/lsp/Pipfile b/lsp/Pipfile index 8a22af0897..fe8041a2cc 100644 --- a/lsp/Pipfile +++ b/lsp/Pipfile @@ -4,8 +4,8 @@ verify_ssl = true name = "pypi" [packages] -wmill = ">=1.380.0" -wmill_pg = ">=1.380.0" +wmill = ">=1.381.0" +wmill_pg = ">=1.381.0" sendgrid = "*" mysql-connector-python = "*" pymongo = "*" diff --git a/openflow.openapi.yaml b/openflow.openapi.yaml index d615b413b4..935672229f 100644 --- a/openflow.openapi.yaml +++ b/openflow.openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.3" info: - version: 1.380.0 + version: 1.381.0 title: OpenFlow Spec contact: name: Ruben Fiszel diff --git a/powershell-client/WindmillClient/WindmillClient.psd1 b/powershell-client/WindmillClient/WindmillClient.psd1 index 0bcfd5576c..638344529e 100644 --- a/powershell-client/WindmillClient/WindmillClient.psd1 +++ b/powershell-client/WindmillClient/WindmillClient.psd1 @@ -12,7 +12,7 @@ RootModule = 'WindmillClient.psm1' # Version number of this module. -ModuleVersion = '1.380.0' +ModuleVersion = '1.381.0' # Supported PSEditions # CompatiblePSEditions = @() diff --git a/python-client/wmill/pyproject.toml b/python-client/wmill/pyproject.toml index c0c4d05e87..07d2c21fe9 100644 --- a/python-client/wmill/pyproject.toml +++ b/python-client/wmill/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "wmill" -version = "1.380.0" +version = "1.381.0" description = "A client library for accessing Windmill server wrapping the Windmill client API" license = "Apache-2.0" homepage = "https://windmill.dev" diff --git a/python-client/wmill_pg/pyproject.toml b/python-client/wmill_pg/pyproject.toml index e3a85960d1..6f51fab915 100644 --- a/python-client/wmill_pg/pyproject.toml +++ b/python-client/wmill_pg/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "wmill-pg" -version = "1.380.0" +version = "1.381.0" description = "An extension client for the wmill client library focused on pg" license = "Apache-2.0" homepage = "https://windmill.dev" diff --git a/typescript-client/jsr.json b/typescript-client/jsr.json index 311f7343cd..8f9d365208 100644 --- a/typescript-client/jsr.json +++ b/typescript-client/jsr.json @@ -1,6 +1,6 @@ { "name": "@windmill/windmill", - "version": "1.380.0", + "version": "1.381.0", "exports": "./src/index.ts", "publish": { "exclude": ["!src", "./s3Types.ts", "./client.ts"] diff --git a/typescript-client/package.json b/typescript-client/package.json index 80f3aedfa1..11c14fa58a 100644 --- a/typescript-client/package.json +++ b/typescript-client/package.json @@ -1,7 +1,7 @@ { "name": "windmill-client", "description": "Windmill SDK client for browsers and Node.js", - "version": "1.380.0", + "version": "1.381.0", "author": "Ruben Fiszel", "license": "Apache 2.0", "devDependencies": { diff --git a/version.txt b/version.txt index 7c2b74d6ee..74c015aee9 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.380.0 +1.381.0 From 632bd18a2e6171c26311795c7ecae0df6c19dfb6 Mon Sep 17 00:00:00 2001 From: Faton Ramadani Date: Mon, 19 Aug 2024 09:17:56 +0200 Subject: [PATCH 5/5] use xyflow internally instead of svelvet for graph rendering (#4173) * fix(frontend): wip * fix(frontend): wip * fix(frontend): fix while loops + theme * fix(frontend): wip * fix(frontend): wip * fix(frontend): same view as before * fix(frontend): wip * fix(frontend): wip * feat(frontend): flow status viewer * fix(frontend): wip * feat(frontend): migrate to xyflow * feat(frontend): dataflow * feat(frontend): clean up * feat(frontend): clean up * feat(frontend): clean up * feat(frontend): fix min-height * feat(frontend): clean up * feat(frontend): add missing insert button for empty loops * feat(frontend): Fix insert * feat(frontend): Fix branch one * feat(frontend): Support default branch for branch one * feat(frontend): fix add and delete branches * feat(frontend): Make the iterations menu work * feat(frontend): Fix graph height in the flow status viewer * feat(frontend): code improvement * feat(frontend): add missing trigger * feat(frontend): add support for triggers * feat(frontend): fix move * feat(frontend): fix No branch for branch one * feat(frontend): fix light theme * feat(frontend): fix build * feat(frontend): remove dead code * feat(frontend): fix move * feat(frontend): fix vertical alignement * feat(frontend): fix viewport * feat(frontend): style fix * feat(frontend): fix viewgraph * feat(frontend): fix build * feat(frontend): Fix node position * feat(frontend): UI nits * feat(frontend): UI nits * feat(frontend): fix zoom level + fix insert button position * feat(frontend): svelvet clean up * feat(frontend): fix paste button position * feat(frontend): clean up * feat(frontend): migrate Decision Tree * feat(frontend): clean up * feat(frontend): add callback * feat(frontend): migrate Decision Tree * feat(frontend): fix build * feat(frontend): fix branches * feat(frontend): fix branches * feat(frontend): fix insert * feat(frontend): wip * feat(frontend): done * feat(frontend): done * feat(frontend): fix reactivity * feat(frontend): fix z-index issues * feat(frontend): fix bg issue * feat(frontend): fix border status * add padding to flow height * Update FlowGraphV2.svelte --------- Co-authored-by: Ruben Fiszel --- frontend/package-lock.json | 126 +-- frontend/package.json | 1 + .../src/lib/components/FlowGraphViewer.svelte | 6 +- .../components/FlowStatusViewerInner.svelte | 16 +- .../DecisionTreeGraphEditor.svelte | 1 - .../DecisionTreeGraphHeader.svelte | 116 ++- .../DecisionTreeGraphNode.svelte | 146 +-- .../decisionTree/DecisionTreePreview.svelte | 581 ++++++------ .../settingsPanel/decisionTree/nodeHelpers.ts | 10 +- .../components/flows/map/FlowJobsMenu.svelte | 83 +- .../flows/map/FlowModuleSchemaItem.svelte | 3 +- .../flows/map/FlowModuleSchemaMap.svelte | 7 +- .../flows/map/InsertModuleButton.svelte | 25 +- .../lib/components/flows/map/MapItem.svelte | 109 +-- .../components/flows/map/VirtualItem.svelte | 164 +--- .../src/lib/components/graph/FlowGraph.svelte | 892 ------------------ .../lib/components/graph/FlowGraphV2.svelte | 305 ++++++ .../src/lib/components/graph/graphBuilder.ts | 453 +++++++++ frontend/src/lib/components/graph/index.ts | 2 - frontend/src/lib/components/graph/model.ts | 9 - .../graph/renderers/edges/BaseEdge.svelte | 131 +++ .../graph/renderers/edges/DataflowEdge.svelte | 46 + .../graph/renderers/edges/EmptyEdge.svelte | 29 + .../renderers/nodes/BranchAllStart.svelte | 64 ++ .../renderers/nodes/BranchOneStart.svelte | 61 ++ .../renderers/nodes/ForLoopEndNode.svelte | 29 + .../renderers/nodes/ForLoopStartNode.svelte | 31 + .../graph/renderers/nodes/InputNode.svelte | 90 ++ .../graph/renderers/nodes/ModuleNode.svelte | 98 ++ .../graph/renderers/nodes/NoBranchNode.svelte | 33 + .../graph/renderers/nodes/NodeWrapper.svelte | 24 + .../graph/renderers/nodes/ResultNode.svelte | 36 + .../renderers/nodes/branchAllEndNode.svelte | 31 + .../renderers/nodes/branchOneEndNode.svelte | 29 + .../lib/components/graph/renderers/utils.ts | 32 + .../src/lib/components/graph/svelvet/LICENSE | 21 - .../graph/svelvet/container/README.md | 7 - .../container/controllers/middleware.ts | 91 -- .../graph/svelvet/container/models/index.ts | 0 .../svelvet/container/views/GraphView.svelte | 304 ------ .../svelvet/container/views/Svelvet.svelte | 131 --- .../svelvet/customCss/controllers/getCss.ts | 50 - .../graph/svelvet/d3/controllers/d3.ts | 91 -- .../svelvet/edges/controllers/anchorCbDev.ts | 119 --- .../svelvet/edges/controllers/anchorCbUser.ts | 98 -- .../graph/svelvet/edges/controllers/util.ts | 78 -- .../graph/svelvet/edges/models/Anchor.ts | 119 --- .../graph/svelvet/edges/models/Edge.ts | 95 -- .../graph/svelvet/edges/types/types.ts | 19 - .../svelvet/edges/views/Edges/BaseEdge.svelte | 108 --- .../svelvet/edges/views/Edges/EdgeText.svelte | 48 - .../edges/views/Edges/SimpleBezierEdge.svelte | 175 ---- .../edges/views/Edges/SmoothStepEdge.svelte | 157 --- .../svelvet/edges/views/Edges/StepEdge.svelte | 8 - .../graph/svelvet/edges/views/Edges/types.ts | 58 -- .../graph/svelvet/edges/views/Edges/utils.ts | 84 -- .../graph/svelvet/nodes/controllers/util.ts | 16 - .../graph/svelvet/nodes/models/Node.ts | 156 --- .../graph/svelvet/nodes/views/Node.svelte | 91 -- .../svelvet/store/controllers/storeApi.ts | 103 -- .../svelvet/store/controllers/userApi.ts | 18 - .../graph/svelvet/store/controllers/util.ts | 215 ----- .../graph/svelvet/store/models/store.ts | 15 - .../graph/svelvet/store/types/types.ts | 122 --- .../components/graph/svelvet/types/README.md | 3 - .../components/graph/svelvet/types/index.ts | 3 - .../components/graph/svelvet/types/types.ts | 60 -- frontend/src/lib/components/graph/util.ts | 12 +- .../[job]/[resume]/[hmac]/+page.svelte | 4 +- frontend/src/routes/view_graph/+page.svelte | 4 +- 70 files changed, 2097 insertions(+), 4405 deletions(-) delete mode 100644 frontend/src/lib/components/graph/FlowGraph.svelte create mode 100644 frontend/src/lib/components/graph/FlowGraphV2.svelte create mode 100644 frontend/src/lib/components/graph/graphBuilder.ts create mode 100644 frontend/src/lib/components/graph/renderers/edges/BaseEdge.svelte create mode 100644 frontend/src/lib/components/graph/renderers/edges/DataflowEdge.svelte create mode 100644 frontend/src/lib/components/graph/renderers/edges/EmptyEdge.svelte create mode 100644 frontend/src/lib/components/graph/renderers/nodes/BranchAllStart.svelte create mode 100644 frontend/src/lib/components/graph/renderers/nodes/BranchOneStart.svelte create mode 100644 frontend/src/lib/components/graph/renderers/nodes/ForLoopEndNode.svelte create mode 100644 frontend/src/lib/components/graph/renderers/nodes/ForLoopStartNode.svelte create mode 100644 frontend/src/lib/components/graph/renderers/nodes/InputNode.svelte create mode 100644 frontend/src/lib/components/graph/renderers/nodes/ModuleNode.svelte create mode 100644 frontend/src/lib/components/graph/renderers/nodes/NoBranchNode.svelte create mode 100644 frontend/src/lib/components/graph/renderers/nodes/NodeWrapper.svelte create mode 100644 frontend/src/lib/components/graph/renderers/nodes/ResultNode.svelte create mode 100644 frontend/src/lib/components/graph/renderers/nodes/branchAllEndNode.svelte create mode 100644 frontend/src/lib/components/graph/renderers/nodes/branchOneEndNode.svelte create mode 100644 frontend/src/lib/components/graph/renderers/utils.ts delete mode 100644 frontend/src/lib/components/graph/svelvet/LICENSE delete mode 100644 frontend/src/lib/components/graph/svelvet/container/README.md delete mode 100644 frontend/src/lib/components/graph/svelvet/container/controllers/middleware.ts delete mode 100644 frontend/src/lib/components/graph/svelvet/container/models/index.ts delete mode 100644 frontend/src/lib/components/graph/svelvet/container/views/GraphView.svelte delete mode 100644 frontend/src/lib/components/graph/svelvet/container/views/Svelvet.svelte delete mode 100644 frontend/src/lib/components/graph/svelvet/customCss/controllers/getCss.ts delete mode 100644 frontend/src/lib/components/graph/svelvet/d3/controllers/d3.ts delete mode 100644 frontend/src/lib/components/graph/svelvet/edges/controllers/anchorCbDev.ts delete mode 100644 frontend/src/lib/components/graph/svelvet/edges/controllers/anchorCbUser.ts delete mode 100644 frontend/src/lib/components/graph/svelvet/edges/controllers/util.ts delete mode 100644 frontend/src/lib/components/graph/svelvet/edges/models/Anchor.ts delete mode 100644 frontend/src/lib/components/graph/svelvet/edges/models/Edge.ts delete mode 100644 frontend/src/lib/components/graph/svelvet/edges/types/types.ts delete mode 100644 frontend/src/lib/components/graph/svelvet/edges/views/Edges/BaseEdge.svelte delete mode 100644 frontend/src/lib/components/graph/svelvet/edges/views/Edges/EdgeText.svelte delete mode 100644 frontend/src/lib/components/graph/svelvet/edges/views/Edges/SimpleBezierEdge.svelte delete mode 100644 frontend/src/lib/components/graph/svelvet/edges/views/Edges/SmoothStepEdge.svelte delete mode 100644 frontend/src/lib/components/graph/svelvet/edges/views/Edges/StepEdge.svelte delete mode 100644 frontend/src/lib/components/graph/svelvet/edges/views/Edges/types.ts delete mode 100644 frontend/src/lib/components/graph/svelvet/edges/views/Edges/utils.ts delete mode 100644 frontend/src/lib/components/graph/svelvet/nodes/controllers/util.ts delete mode 100644 frontend/src/lib/components/graph/svelvet/nodes/models/Node.ts delete mode 100644 frontend/src/lib/components/graph/svelvet/nodes/views/Node.svelte delete mode 100644 frontend/src/lib/components/graph/svelvet/store/controllers/storeApi.ts delete mode 100644 frontend/src/lib/components/graph/svelvet/store/controllers/userApi.ts delete mode 100644 frontend/src/lib/components/graph/svelvet/store/controllers/util.ts delete mode 100644 frontend/src/lib/components/graph/svelvet/store/models/store.ts delete mode 100644 frontend/src/lib/components/graph/svelvet/store/types/types.ts delete mode 100644 frontend/src/lib/components/graph/svelvet/types/README.md delete mode 100644 frontend/src/lib/components/graph/svelvet/types/index.ts delete mode 100644 frontend/src/lib/components/graph/svelvet/types/types.ts diff --git a/frontend/package-lock.json b/frontend/package-lock.json index b4b09e7aa9..2362fcdcf4 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -16,6 +16,7 @@ "@redocly/json-to-json-schema": "^0.0.1", "@tanstack/svelte-table": "^8.9.9", "@windmill-labs/svelte-dnd-action": "latest", + "@xyflow/svelte": "^0.1.14", "ag-charts-community": "^9.0.1", "ag-charts-enterprise": "^9.0.1", "ag-grid-community": "^31.0.0", @@ -1778,6 +1779,14 @@ "resolved": "https://registry.npmjs.org/@streamparser/json/-/json-0.0.20.tgz", "integrity": "sha512-VqAAkydywPpkw63WQhPVKCD3SdwXuihCUVZbbiY3SfSTGQyHmwRoq27y4dmJdZuJwd5JIlQoMPyGvMbUPY0RKQ==" }, + "node_modules/@svelte-put/shortcut": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@svelte-put/shortcut/-/shortcut-3.1.1.tgz", + "integrity": "sha512-2L5EYTZXiaKvbEelVkg5znxqvfZGZai3m97+cAiUBhLZwXnGtviTDpHxOoZBsqz41szlfRMcamW/8o0+fbW3ZQ==", + "peerDependencies": { + "svelte": "^3.55.0 || ^4.0.0 || ^5.0.0" + } + }, "node_modules/@sveltejs/adapter-static": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-3.0.1.tgz", @@ -2023,8 +2032,7 @@ "node_modules/@types/d3-color": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", - "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", - "dev": true + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==" }, "node_modules/@types/d3-contour": { "version": "3.0.6", @@ -2052,7 +2060,6 @@ "version": "3.0.7", "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", - "dev": true, "dependencies": { "@types/d3-selection": "*" } @@ -2109,7 +2116,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", - "dev": true, "dependencies": { "@types/d3-color": "*" } @@ -2156,8 +2162,7 @@ "node_modules/@types/d3-selection": { "version": "3.0.10", "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.10.tgz", - "integrity": "sha512-cuHoUgS/V3hLdjJOLTT691+G2QoqAjCVLmr4kJXR4ha56w1Zdu8UUQ5TxLRqudgNjwXeQxKMq4j+lyf9sWuslg==", - "dev": true + "integrity": "sha512-cuHoUgS/V3hLdjJOLTT691+G2QoqAjCVLmr4kJXR4ha56w1Zdu8UUQ5TxLRqudgNjwXeQxKMq4j+lyf9sWuslg==" }, "node_modules/@types/d3-shape": { "version": "3.1.5", @@ -2190,7 +2195,6 @@ "version": "3.0.8", "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.8.tgz", "integrity": "sha512-ew63aJfQ/ms7QQ4X7pk5NxQ9fZH/z+i24ZfJ6tJSfqxJMrYLiK01EAs2/Rtw/JreGUsS3pLPNV644qXFGnoZNQ==", - "dev": true, "dependencies": { "@types/d3-selection": "*" } @@ -2199,7 +2203,6 @@ "version": "3.0.8", "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", - "dev": true, "dependencies": { "@types/d3-interpolate": "*", "@types/d3-selection": "*" @@ -2291,8 +2294,7 @@ "node_modules/@types/pug": { "version": "2.0.9", "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.9.tgz", - "integrity": "sha512-Yg4LkgFYvn1faISbDNWmcAC1XoDT8IoMUFspp5mnagKk+UvD2N0IWt5A7GRdMubsNWqgCLmrkf8rXkzNqb4szA==", - "dev": true + "integrity": "sha512-Yg4LkgFYvn1faISbDNWmcAC1XoDT8IoMUFspp5mnagKk+UvD2N0IWt5A7GRdMubsNWqgCLmrkf8rXkzNqb4szA==" }, "node_modules/@types/semver": { "version": "7.5.5", @@ -2512,6 +2514,34 @@ "svelte": ">=3.23.0 || ^5.0.0-next.0" } }, + "node_modules/@xyflow/svelte": { + "version": "0.1.14", + "resolved": "https://registry.npmjs.org/@xyflow/svelte/-/svelte-0.1.14.tgz", + "integrity": "sha512-DdlFz+JKW1sQt8Q8O1DoSsroTvFk1RzmpcEZcGARt1REbnLy4BYG/03piaiclaC4iHK6feaPDvxpF057CkMCTw==", + "dependencies": { + "@svelte-put/shortcut": "^3.1.0", + "@xyflow/system": "0.0.37", + "classcat": "^5.0.4", + "svelte-preprocess": "^5.1.3" + }, + "peerDependencies": { + "svelte": "^3.0.0 || ^4.0.0" + } + }, + "node_modules/@xyflow/system": { + "version": "0.0.37", + "resolved": "https://registry.npmjs.org/@xyflow/system/-/system-0.0.37.tgz", + "integrity": "sha512-hSIhezhxgftPUpC+xiQVIorcRILZUOWlLjpYPTyGWRu8s4RJvM4GqvrsFmD5OnMKXLgpU7/PqqUibDVO67oWQQ==", + "dependencies": { + "@types/d3-drag": "^3.0.7", + "@types/d3-selection": "^3.0.10", + "@types/d3-transition": "^3.0.8", + "@types/d3-zoom": "^3.0.8", + "d3-drag": "^3.0.0", + "d3-selection": "^3.0.0", + "d3-zoom": "^3.0.0" + } + }, "node_modules/@zerodevx/svelte-toast": { "version": "0.9.5", "resolved": "https://registry.npmjs.org/@zerodevx/svelte-toast/-/svelte-toast-0.9.5.tgz", @@ -2974,7 +3004,6 @@ "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", - "dev": true, "engines": { "node": "*" } @@ -3242,6 +3271,11 @@ "consola": "^3.2.3" } }, + "node_modules/classcat": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.5.tgz", + "integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==" + }, "node_modules/clone": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", @@ -3309,8 +3343,7 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "devOptional": true + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/confbox": { "version": "0.1.6", @@ -3922,7 +3955,6 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", - "dev": true, "engines": { "node": ">=8" } @@ -4149,8 +4181,7 @@ "node_modules/es6-promise": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", - "integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==", - "dev": true + "integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==" }, "node_modules/esbuild": { "version": "0.21.5", @@ -4844,8 +4875,7 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "devOptional": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { "version": "2.3.3", @@ -4963,7 +4993,6 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "devOptional": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -4995,7 +5024,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "devOptional": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5005,7 +5033,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "devOptional": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -5122,8 +5149,7 @@ "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, "node_modules/graphemer": { "version": "1.4.0", @@ -5431,7 +5457,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "devOptional": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -5440,8 +5465,7 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "devOptional": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/ini": { "version": "1.3.8", @@ -5921,7 +5945,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", - "dev": true, + "devOptional": true, "engines": { "node": ">=10" } @@ -7000,7 +7024,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true, "engines": { "node": ">=4" } @@ -7089,7 +7112,6 @@ "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, "dependencies": { "minimist": "^1.2.6" }, @@ -7202,7 +7224,7 @@ "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "dev": true, + "devOptional": true, "funding": [ { "type": "github", @@ -7511,7 +7533,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "devOptional": true, "dependencies": { "wrappy": "1" } @@ -7708,7 +7729,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "devOptional": true, "engines": { "node": ">=0.10.0" } @@ -7796,8 +7816,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", - "dev": true, - "license": "ISC" + "devOptional": true }, "node_modules/picomatch-browser": { "version": "2.2.6", @@ -7887,7 +7906,7 @@ "version": "8.4.41", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz", "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==", - "dev": true, + "devOptional": true, "funding": [ { "type": "opencollective", @@ -7902,7 +7921,6 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.0.1", @@ -8063,7 +8081,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz", "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==", - "dev": true, + "devOptional": true, "dependencies": { "lilconfig": "^2.0.5", "yaml": "^2.1.1" @@ -9030,7 +9048,6 @@ "version": "0.5.1", "resolved": "https://registry.npmjs.org/sander/-/sander-0.5.1.tgz", "integrity": "sha512-3lVqBir7WuKDHGrKRDn/1Ye3kwpXaDOMsiRP1wd6wpZW56gJhsbp5RqQpA6JG/P+pkXizygnr1dKR8vzWaVsfA==", - "dev": true, "dependencies": { "es6-promise": "^3.1.2", "graceful-fs": "^4.1.3", @@ -9042,7 +9059,6 @@ "version": "2.7.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, "dependencies": { "glob": "^7.1.3" }, @@ -9212,7 +9228,6 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/sorcery/-/sorcery-0.11.0.tgz", "integrity": "sha512-J69LQ22xrQB1cIFJhPfgtLuI6BpWRiWu1Y3vSsIwK/eAScqJxd/+CJlUuHQRdX2C9NGFamq+KqNywGgaThwfHw==", - "dev": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.14", "buffer-crc32": "^0.2.5", @@ -9867,27 +9882,26 @@ } }, "node_modules/svelte-preprocess": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-5.1.0.tgz", - "integrity": "sha512-EkErPiDzHAc0k2MF5m6vBNmRUh338h2myhinUw/xaqsLs7/ZvsgREiLGj03VrSzbY/TB5ZXgBOsKraFee5yceA==", - "dev": true, + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-5.1.4.tgz", + "integrity": "sha512-IvnbQ6D6Ao3Gg6ftiM5tdbR6aAETwjhHV+UKGf5bHGYR69RQvF1ho0JKPcbUON4vy4R7zom13jPjgdOWCQ5hDA==", "hasInstallScript": true, "dependencies": { "@types/pug": "^2.0.6", "detect-indent": "^6.1.0", - "magic-string": "^0.27.0", + "magic-string": "^0.30.5", "sorcery": "^0.11.0", "strip-indent": "^3.0.0" }, "engines": { - "node": ">= 14.10.0" + "node": ">= 16.0.0" }, "peerDependencies": { "@babel/core": "^7.10.2", "coffeescript": "^2.5.1", "less": "^3.11.3 || ^4.0.0", "postcss": "^7 || ^8", - "postcss-load-config": "^2.1.0 || ^3.0.0 || ^4.0.0", + "postcss-load-config": "^2.1.0 || ^3.0.0 || ^4.0.0 || ^5.0.0", "pug": "^3.0.0", "sass": "^1.26.8", "stylus": "^0.55.0", @@ -9928,23 +9942,10 @@ } } }, - "node_modules/svelte-preprocess/node_modules/magic-string": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz", - "integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==", - "dev": true, - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.13" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/svelte-preprocess/node_modules/strip-indent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, "dependencies": { "min-indent": "^1.0.0" }, @@ -10324,7 +10325,7 @@ "version": "5.2.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", - "dev": true, + "devOptional": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -10787,8 +10788,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "devOptional": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/write-file-atomic": { "version": "5.0.1", diff --git a/frontend/package.json b/frontend/package.json index 2f5ce6b2fd..26140e7f23 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -97,6 +97,7 @@ "@redocly/json-to-json-schema": "^0.0.1", "@tanstack/svelte-table": "^8.9.9", "@windmill-labs/svelte-dnd-action": "latest", + "@xyflow/svelte": "^0.1.14", "ag-charts-community": "^9.0.1", "ag-charts-enterprise": "^9.0.1", "ag-grid-community": "^31.0.0", diff --git a/frontend/src/lib/components/FlowGraphViewer.svelte b/frontend/src/lib/components/FlowGraphViewer.svelte index 6edeea3596..b81b1184c2 100644 --- a/frontend/src/lib/components/FlowGraphViewer.svelte +++ b/frontend/src/lib/components/FlowGraphViewer.svelte @@ -1,12 +1,11 @@ {#if notAnonynmous} @@ -905,7 +902,7 @@ {#if render} {#if job.raw_flow && !isListJob}
-
+
{#each Object.values($retryStatus) as count} @@ -924,8 +921,9 @@ {/each}
- { @@ -950,6 +948,7 @@ }} on:selectedIteration={(e) => { let detail = e.detail + setModuleState(detail.moduleId, { selectedForloop: detail.id, selectedForloopIndex: detail.index @@ -957,7 +956,6 @@ globalRefreshes[detail.moduleId]?.({ job: detail.id, index: detail.index }) }} modules={job.raw_flow?.modules ?? []} - failureModule={job.raw_flow?.failure_module} />
{ diff --git a/frontend/src/lib/components/apps/editor/settingsPanel/DecisionTreeGraphHeader.svelte b/frontend/src/lib/components/apps/editor/settingsPanel/DecisionTreeGraphHeader.svelte index 21bad417bd..0a3302814f 100644 --- a/frontend/src/lib/components/apps/editor/settingsPanel/DecisionTreeGraphHeader.svelte +++ b/frontend/src/lib/components/apps/editor/settingsPanel/DecisionTreeGraphHeader.svelte @@ -1,65 +1,81 @@ -
-
-
{label}
-
- - {#if canDelete} -
- -
- {/if} - - {#if node.id !== 'end' && editable} + +
- dispatch('nodeInsert')} - on:addBranch={() => dispatch('addBranch')} - canAddBranch={false} - /> +
{data.node.label}
- {/if} -
+ + {#if data.canDelete} +
+ +
+ {/if} + + {#if data.node.id !== 'end'} +
+ { + data.nodeCallbackHandler( + 'nodeInsert', + data.node.id, + data.node, + data.parentIds ?? [], + data.branchHeader + ) + }} + on:addBranch={() => { + data.nodeCallbackHandler('addBranch', data.node.id, data.node, data.parentIds, true) + }} + canAddBranch={false} + /> +
+ {/if} +
+ diff --git a/frontend/src/lib/components/apps/editor/settingsPanel/DecisionTreeGraphNode.svelte b/frontend/src/lib/components/apps/editor/settingsPanel/DecisionTreeGraphNode.svelte index 4ab3578b41..53d23e7a88 100644 --- a/frontend/src/lib/components/apps/editor/settingsPanel/DecisionTreeGraphNode.svelte +++ b/frontend/src/lib/components/apps/editor/settingsPanel/DecisionTreeGraphNode.svelte @@ -1,5 +1,5 @@ -
- - - {#if canDelete} - - {/if} +
+ {data.node.label === '' ? `Tab: ${data.node.id}` : data.node.label} +
+ + Tab index: {data.index} + + You can manually select a node using the setTab function with this index in a frontend + runnable. + + + - {#if node.id !== 'end'} -
- dispatch('nodeInsert')} - on:addBranch={() => dispatch('addBranch')} - canAddBranch={canAddBranch || node.next.length > 1} - canAddNode={node.next.length <= 1} - /> -
- {/if} -
+ {#if data.canDelete} + + {/if} + + {#if data.node.id !== 'end'} +
+ { + data.nodeCallbackHandler('nodeInsert', data.node.id, data.node, data.parentIds, false) + }} + on:addBranch={() => { + data.nodeCallbackHandler('addBranch', data.node.id, data.node, data.parentIds, true) + }} + canAddBranch={data.canAddBranch || data.node.next.length > 1} + canAddNode={data.node.next.length <= 1} + /> +
+ {/if} +
+ diff --git a/frontend/src/lib/components/apps/editor/settingsPanel/decisionTree/DecisionTreePreview.svelte b/frontend/src/lib/components/apps/editor/settingsPanel/decisionTree/DecisionTreePreview.svelte index 9408962449..ab83790a41 100644 --- a/frontend/src/lib/components/apps/editor/settingsPanel/decisionTree/DecisionTreePreview.svelte +++ b/frontend/src/lib/components/apps/editor/settingsPanel/decisionTree/DecisionTreePreview.svelte @@ -1,16 +1,15 @@ - + +
-{#if mounted} - -{/if} + + diff --git a/frontend/src/lib/components/apps/editor/settingsPanel/decisionTree/nodeHelpers.ts b/frontend/src/lib/components/apps/editor/settingsPanel/decisionTree/nodeHelpers.ts index cdb285c5bf..4b6949daa4 100644 --- a/frontend/src/lib/components/apps/editor/settingsPanel/decisionTree/nodeHelpers.ts +++ b/frontend/src/lib/components/apps/editor/settingsPanel/decisionTree/nodeHelpers.ts @@ -1,4 +1,5 @@ -import { NODE, type Node } from '$lib/components/graph' +import { NODE } from '$lib/components/graph' +import { type Node } from '@xyflow/svelte' interface NodeConfig { id: string @@ -35,12 +36,7 @@ export function createNode(nodeConfig: NodeConfig): Node { position: nodeConfig.position || { x: -1, y: -1 }, data: nodeConfig.data, width: nodeConfig.width || NODE.width, - height: nodeConfig.height || NODE.height, - borderColor: nodeConfig.borderColor || '#999', - sourcePosition: 'bottom', - targetPosition: 'top', - parentIds: nodeConfig.parentIds || [], - loopDepth: nodeConfig.loopDepth || 0 + height: nodeConfig.height || NODE.height } } diff --git a/frontend/src/lib/components/flows/map/FlowJobsMenu.svelte b/frontend/src/lib/components/flows/map/FlowJobsMenu.svelte index 98f9645648..92a9e2619c 100644 --- a/frontend/src/lib/components/flows/map/FlowJobsMenu.svelte +++ b/frontend/src/lib/components/flows/map/FlowJobsMenu.svelte @@ -1,7 +1,8 @@ - - -
-
- - {#each flowJobs ?? [] as id, idx (id)} - {#if filter == undefined || (idx + 1).toString().includes(filter.toString())} - - {/if} - {/each} -
+ ? 'text-red-400' + : 'text-secondary'}" + > + #{selected == -1 ? '?' : selected + 1} + +
+ + + + {#each flowJobs ?? [] as id, idx (id)} + {#if filter == undefined || (idx + 1).toString().includes(filter.toString())} + + + + {/if} + {/each}
diff --git a/frontend/src/lib/components/flows/map/FlowModuleSchemaItem.svelte b/frontend/src/lib/components/flows/map/FlowModuleSchemaItem.svelte index 932e3f1470..c0d25c1b19 100644 --- a/frontend/src/lib/components/flows/map/FlowModuleSchemaItem.svelte +++ b/frontend/src/lib/components/flows/map/FlowModuleSchemaItem.svelte @@ -147,9 +147,8 @@ > {#if $$slots.icon} - {/if} -
{label}
+
{label}
{#if id} {id} diff --git a/frontend/src/lib/components/flows/map/FlowModuleSchemaMap.svelte b/frontend/src/lib/components/flows/map/FlowModuleSchemaMap.svelte index b0c1ebfa4c..4be72b7170 100644 --- a/frontend/src/lib/components/flows/map/FlowModuleSchemaMap.svelte +++ b/frontend/src/lib/components/flows/map/FlowModuleSchemaMap.svelte @@ -16,7 +16,6 @@ import FlowConstantsItem from './FlowConstantsItem.svelte' import { dfs } from '../dfs' - import { FlowGraph } from '$lib/components/graph' import FlowErrorHandlerItem from './FlowErrorHandlerItem.svelte' import { push } from '$lib/history' import ConfirmationModal from '$lib/components/common/confirmationModal/ConfirmationModal.svelte' @@ -30,6 +29,7 @@ import FlowTutorials from '$lib/components/FlowTutorials.svelte' import { ignoredTutorials } from '$lib/components/tutorials/ignoredTutorials' import { tutorialInProgress } from '$lib/tutorialUtils' + import FlowGraphV2 from '$lib/components/graph/FlowGraphV2.svelte' export let modules: FlowModule[] | undefined export let sidebarSize: number | undefined = undefined @@ -252,13 +252,12 @@
- { push(history, $flowStore) + selectNextId(e.id) + removeAtId($flowStore.value.modules, e.id) if ($flowInputsStore) { diff --git a/frontend/src/lib/components/flows/map/InsertModuleButton.svelte b/frontend/src/lib/components/flows/map/InsertModuleButton.svelte index 3d4d5b3573..6589dbbb71 100644 --- a/frontend/src/lib/components/flows/map/InsertModuleButton.svelte +++ b/frontend/src/lib/components/flows/map/InsertModuleButton.svelte @@ -6,6 +6,7 @@ import type { FlowModule } from '$lib/gen' import BarsStaggered from '$lib/components/icons/BarsStaggered.svelte' import type { WhitelabelCustomUi } from '$lib/components/custom_ui' + import { twMerge } from 'tailwind-merge' const dispatch = createEventDispatcher() export let trigger = false @@ -28,15 +29,21 @@ placement="bottom-center" let:close > - + + +
diff --git a/frontend/src/lib/components/flows/map/MapItem.svelte b/frontend/src/lib/components/flows/map/MapItem.svelte index a617520781..9457c15c78 100644 --- a/frontend/src/lib/components/flows/map/MapItem.svelte +++ b/frontend/src/lib/components/flows/map/MapItem.svelte @@ -3,36 +3,23 @@ import LanguageIcon from '$lib/components/common/languageIcons/LanguageIcon.svelte' import IconedResourceType from '$lib/components/IconedResourceType.svelte' import type { FlowModule } from '$lib/gen' - import { - Building, - ClipboardCopy, - GitBranchPlus, - Repeat, - Square, - ArrowDown, - GitBranch - } from 'lucide-svelte' + import { Building, Repeat, Square, ArrowDown, GitBranch } from 'lucide-svelte' import { createEventDispatcher, getContext } from 'svelte' import type { Writable } from 'svelte/store' import FlowModuleSchemaItem from './FlowModuleSchemaItem.svelte' - import InsertModuleButton from './InsertModuleButton.svelte' import { prettyLanguage } from '$lib/common' import { msToSec } from '$lib/utils' import BarsStaggered from '$lib/components/icons/BarsStaggered.svelte' import FlowJobsMenu from './FlowJobsMenu.svelte' export let mod: FlowModule - export let trigger: boolean export let insertable: boolean - export let insertableEnd = false export let annotation: string | undefined = undefined - export let branchable: boolean = false export let bgColor: string = '' export let modules: FlowModule[] export let moving: string | undefined = undefined export let duration_ms: number | undefined = undefined - export let disableAi: boolean = false - export let wrapperId: string | undefined = undefined + export let retries: number | undefined = undefined export let flowJobs: | { flowJobs: string[]; selected: number; flowJobsSuccess: (boolean | undefined)[] } @@ -69,46 +56,9 @@ function onDelete(event: CustomEvent) { dispatch('delete', event) } - let openMenu: boolean | undefined = undefined - let openMenu2: boolean | undefined = undefined {#if mod} - {#if insertable} -
- {#if moving} - - {:else} - { - dispatch('insert', { modules, index: idx + 1, detail: 'script', script: e.detail }) - }} - on:new={(e) => { - dispatch('insert', { modules, index: idx, detail: e.detail }) - }} - index={idx} - {modules} - /> - {/if} -
- {/if}
{#if moving == mod.id}
@@ -128,8 +78,8 @@ {annotation}
{/if} - {#if flowJobs && !insertable} -
+ {#if flowJobs && !insertable && (mod.value.type === 'forloopflow' || mod.value.type === 'whileloopflow')} +
{ dispatch('selectedIteration', e.detail) @@ -242,55 +192,4 @@ {/if}
- {#if insertable && insertableEnd} -
- {#if moving} - - {:else} - { - dispatch('insert', { modules, index: idx + 1, detail: 'script', script: e.detail }) - }} - on:new={(e) => { - dispatch('insert', { modules, index: idx + 1, detail: e.detail }) - }} - index={idx + 1} - {modules} - /> - {/if} -
- {/if} - - {#if insertable && branchable} -
- -
- {/if} {/if} diff --git a/frontend/src/lib/components/flows/map/VirtualItem.svelte b/frontend/src/lib/components/flows/map/VirtualItem.svelte index 2bcad4e6b4..f292523741 100644 --- a/frontend/src/lib/components/flows/map/VirtualItem.svelte +++ b/frontend/src/lib/components/flows/map/VirtualItem.svelte @@ -1,29 +1,16 @@ -{#if insertable && deleteBranch} -
- -
-{/if}
{ if (selectable) { if (id) { @@ -93,7 +61,7 @@ {/if}
-
{label}
+
{label}
{#if id && !hideId} {id} @@ -101,125 +69,3 @@
- -{#if insertable && modules && (label != 'Input' || modules.length == 0)} -
- {#if moving} - - {:else} - { - if (modules) { - dispatch('insert', { - modules, - index, - detail: 'script', - script: e.detail - }) - } - }} - on:new={(e) => { - if (modules) { - dispatch('insert', { - modules, - index: index, - detail: e.detail - }) - } - }} - {index} - modules={modules ?? []} - /> - {/if} -
-{/if} - -{#if insertable && modules && label == 'Input'} - {#if !disableAi} -
- - - {#if !$copilotInfo.exists_openai_resource_path} -
-

- Enable Windmill AI in the - { - close() - }} - > - workspace settings - - -

-
- {/if} -
-
- {/if} -
- { - if (modules) { - dispatch('insert', { - modules, - index: 0, - detail: e.detail - }) - } - }} - index={0} - modules={modules ?? []} - /> -
-{/if} diff --git a/frontend/src/lib/components/graph/FlowGraph.svelte b/frontend/src/lib/components/graph/FlowGraph.svelte deleted file mode 100644 index 17e447dbf0..0000000000 --- a/frontend/src/lib/components/graph/FlowGraph.svelte +++ /dev/null @@ -1,892 +0,0 @@ - - - - - -
- {#if !error} - {#if width && height} - {#key renderCount} - { - try { - localStorage.setItem('svelvet', encodeState({ modules, failureModule })) - } catch (e) { - console.error('error interacting with local storage', e) - } - window.open('/view_graph', '_blank') - }} - {download} - highlightEdges={false} - locked - bind:dataflow - {nodes} - width={fullSize ? fullWidth : width} - {edges} - {height} - {scroll} - nodeSelected={showDataflow} - background={false} - /> - {/key} - {/if} - {:else} -
- Error computing the graph: {error} -
- {/if} -
diff --git a/frontend/src/lib/components/graph/FlowGraphV2.svelte b/frontend/src/lib/components/graph/FlowGraphV2.svelte new file mode 100644 index 0000000000..46d0a0c11d --- /dev/null +++ b/frontend/src/lib/components/graph/FlowGraphV2.svelte @@ -0,0 +1,305 @@ + + +
+ +
+ + {#if download} + { + try { + localStorage.setItem('svelvet', encodeState({ modules, failureModule })) + } catch (e) { + console.error('error interacting with local storage', e) + } + window.open('/view_graph', '_blank') + }} + class="!bg-surface" + > + + + {/if} + + + + {#if showDataflow} + { + $useDataflow = !$useDataflow + }} + size="xs" + options={{ + right: 'Dataflow' + }} + /> + {/if} + + +
+ + diff --git a/frontend/src/lib/components/graph/graphBuilder.ts b/frontend/src/lib/components/graph/graphBuilder.ts new file mode 100644 index 0000000000..0475c1271e --- /dev/null +++ b/frontend/src/lib/components/graph/graphBuilder.ts @@ -0,0 +1,453 @@ +import type { FlowModule } from '$lib/gen' +import { type Node, type Edge } from '@xyflow/svelte' +import { getDependeeAndDependentComponents } from '../flows/flowExplorer' +import { dfsByModule } from '../flows/previousResults' +import { defaultIfEmptyString } from '$lib/utils' + +export type GraphEventHandlers = { + insert: (detail) => void + deleteBranch: (detail, label: string) => void + select: (modId: string) => void + delete: (detail, label: string) => void + newBranch: (module: FlowModule) => void + move: (module: FlowModule, modules: FlowModule[]) => void + selectedIteration: (detail, moduleId: string) => void +} + +export default function graphBuilder( + modules: FlowModule[] | undefined, + extra: Record, + failureModule: FlowModule | undefined, + eventHandlers: GraphEventHandlers, + success: boolean | undefined, + useDataflow: boolean | undefined, + selectedId: string | undefined, + moving: string | undefined +): { + nodes: Node[] + edges: Edge[] +} { + const nodes: Node[] = [] + const edges: Edge[] = [] + + if (!modules) { + return { nodes, edges } + } + + function addNode(module: FlowModule, offset: number, type: string, subModules?: FlowModule[]) { + nodes.push({ + id: module.id, + data: { + value: module.value, + offset: offset, + module: module, + modules: subModules ?? modules, + parentIds: [], + eventHandlers: eventHandlers, + moving: moving, + ...extra + }, + position: { x: -1, y: -1 }, + type: type + }) + + return module.id + } + + const parents: { [key: string]: string[] } = {} + + function addEdge( + sourceId: string, + targetId: string, + options?: { + customId?: string + type?: string + subModules?: FlowModule[] + disableMoveIds?: string[] + } + ) { + parents[targetId] = [...(parents[targetId] ?? []), sourceId] + + // Find the index of the target module in the modules array + const mods = options?.subModules ?? modules + + // Index of the target module in the modules array + let index = mods?.findIndex((m) => m.id === targetId) ?? -1 + + edges.push({ + id: options?.customId || `edge:${sourceId}->${targetId}`, + source: sourceId, + target: targetId, + type: options?.type ?? 'edge', + data: { + insertable: extra.insertable, + modules: options?.subModules ?? modules, + sourceId, + targetId, + moving, + eventHandlers, + disableMoveIds: options?.disableMoveIds, + enableTrigger: sourceId === 'Input', + // If the index is -1, it means that the target module is not in the modules array, so we set it to the length of the array + index: index >= 0 ? index : mods?.length ?? 0, + ...extra + } + }) + } + + const inputNode: Node = { + id: 'Input', + position: { x: -1, y: -1 }, + type: 'input2', + data: { + eventHandlers: eventHandlers, + modules: modules, + + ...extra + } + } + + const resultNode: Node = { + id: 'result', + data: { + eventHandlers: eventHandlers, + modules: modules, + success: success, + ...extra + }, + position: { x: -1, y: -1 }, + type: 'result' + } + + nodes.push(inputNode) + nodes.push(resultNode) + + function processModules( + modules: FlowModule[], + beforeNode: Node, + nextNode: Node, + currentOffset = 0, + disableMoveIds: string[] = [] + ) { + let previousId: string | undefined = undefined + + if (modules.length === 0) { + addEdge(beforeNode.id, nextNode.id, { + subModules: modules, + disableMoveIds + }) + } else { + modules.forEach((module, index) => { + const localDisableMoveIds = [...disableMoveIds, module.id] + + // Add the edge between the previous node and the current one + if (index > 0 && previousId) { + addEdge(previousId, module.id, { + subModules: modules, + disableMoveIds + }) + } + + if (module.value.type === 'branchall') { + // Start + addNode(module, currentOffset, 'module', modules) + + // "Collect result of each branch" node + const endNode = { + id: `${module.id}-end`, + data: { + offset: currentOffset, + id: module.id, + module: module, + modules: modules, + ...extra + }, + position: { x: -1, y: -1 }, + type: 'branchAllEnd' + } + + nodes.push(endNode) + + if (module.value.branches.length === 0) { + // Add a "No branches" node + const startNode = { + id: `${module.id}-branch-0`, + data: { + offset: currentOffset, + id: module.id, + branchIndex: -1, + modules: modules, + eventHandlers: eventHandlers, + ...extra + }, + position: { x: -1, y: -1 }, + type: 'noBranch' + } + + nodes.push(startNode) + + addEdge(module.id, startNode.id, { + type: 'empty' + }) + addEdge(startNode.id, endNode.id, { + type: 'empty' + }) + } else { + module.value.branches.forEach((branch, branchIndex) => { + // Start node by branch + + const startNode = { + id: `${module.id}-branch-${branchIndex}`, + data: { + offset: currentOffset, + label: defaultIfEmptyString(branch.summary, `Branch ${branchIndex + 1}`), + id: module.id, + branchIndex: branchIndex, + modules: modules, + eventHandlers: eventHandlers, + ...extra + }, + position: { x: -1, y: -1 }, + type: 'branchAllStart' + } + + nodes.push(startNode) + + addEdge(module.id, startNode.id, { + type: 'empty' + }) + + processModules(branch.modules, startNode, endNode, currentOffset, localDisableMoveIds) + }) + } + + previousId = endNode.id + } else if (module.value.type === 'forloopflow') { + addNode(module, currentOffset, 'module', modules) + + const startNode = { + id: `${module.id}-start`, + data: { + offset: currentOffset + 25, + id: module.id, + module: module, + modules: modules, + eventHandlers: eventHandlers, + ...extra + }, + position: { x: -1, y: -1 }, + type: 'forLoopStart' + } + + addEdge(module.id, startNode.id, { + type: 'empty' + }) + + const endNode = { + id: `${module.id}-end`, + data: { + offset: currentOffset, + id: module.id, + module: module, + modules: modules, + eventHandlers: eventHandlers, + ...extra + }, + position: { x: -1, y: -1 }, + type: 'forLoopEnd' + } + + nodes.push(startNode) + nodes.push(endNode) + + processModules( + module.value.modules, + startNode, + endNode, + currentOffset + 25, + localDisableMoveIds + ) + + previousId = endNode.id + } else if (module.value.type === 'whileloopflow') { + addNode(module, currentOffset, 'module', modules) + + const startNode = { + id: `${module.id}-start`, + data: { + offset: currentOffset + 25, + module: module, + modules: modules, + eventHandlers: eventHandlers, + ...extra + }, + position: { x: -1, y: -1 }, + type: 'whileLoopStart' + } + addEdge(module.id, startNode.id, { + type: 'empty' + }) + + const endNode = { + id: `${module.id}-end`, + data: { offset: currentOffset, module: module, modules: modules, ...extra }, + position: { x: -1, y: -1 }, + type: 'whileLoopEnd' + } + + nodes.push(startNode) + nodes.push(endNode) + + processModules( + module.value.modules, + startNode, + endNode, + currentOffset + 25, + localDisableMoveIds + ) + + previousId = endNode.id + } else if (module.value.type === 'branchone') { + addNode(module, currentOffset, 'module', modules) + + const endNode = { + id: `${module.id}-end`, + data: { offset: currentOffset, eventHandlers: eventHandlers }, + position: { x: -1, y: -1 }, + type: 'branchOneEnd' + } + nodes.push(endNode) + + // Add default branch + const defaultBranch = { + id: `${module.id}-default`, + data: { + offset: currentOffset, + label: 'Default', + id: module.id, + branchIndex: -1, + modules: module.value.default, + eventHandlers: eventHandlers, + ...extra + }, + position: { x: -1, y: -1 }, + type: 'noBranch' + } + + nodes.push(defaultBranch) + + addEdge(module.id, defaultBranch.id, { type: 'empty' }) + + processModules( + module.value.default, + defaultBranch, + endNode, + currentOffset, + localDisableMoveIds + ) + + module.value.branches.forEach((branch, branchIndex) => { + // Start node by branch + + const startNode = { + id: `${module.id}-branch-${branchIndex}`, + data: { + offset: currentOffset, + label: + defaultIfEmptyString(branch.summary, 'Branch ' + (branchIndex + 1)) + + '\n`' + + branch.expr + + '`', + id: module.id, + branchIndex: branchIndex, + modules: modules, + eventHandlers: eventHandlers, + ...extra + }, + position: { x: -1, y: -1 }, + type: 'branchOneStart' + } + + nodes.push(startNode) + + addEdge(module.id, startNode.id, { type: 'empty' }) + + processModules(branch.modules, startNode, endNode, currentOffset, localDisableMoveIds) + }) + + previousId = endNode.id + } else { + addNode(module, currentOffset, 'module', modules) + + previousId = module.id + } + + if (index === 0) { + addEdge(beforeNode.id, module.id, { + subModules: modules, + disableMoveIds + }) + } + + if (index === modules.length - 1 && previousId) { + addEdge(previousId, nextNode.id, { + subModules: modules, + disableMoveIds + }) + } + }) + } + } + + processModules(modules, inputNode, resultNode) + + if (failureModule) { + addNode(failureModule, 0, 'module') + } + + Object.keys(parents).forEach((key) => { + const node = nodes.find((n) => n.id === key) + + if (node) { + node.data.parentIds = parents[key] + } + }) + + if (useDataflow && selectedId) { + let deps = getDependeeAndDependentComponents(selectedId, modules ?? [], failureModule) + + if (deps) { + Object.entries(deps.dependees).forEach((x, i) => { + const inputs = x[1] + + inputs?.forEach((input, index) => { + let pid = x[0] + + if (input?.startsWith('flow_input.iter')) { + const parent = dfsByModule(selectedId!, modules ?? [])?.pop() + + if (parent?.id) { + pid = parent.id + } + } + + addEdge(pid, selectedId!, { + customId: `dep-${pid}-${selectedId}-${input}-${index}`, + type: 'dataflowedge' + }) + }) + }) + + Object.entries(deps.dependents).forEach((x, i) => { + let pid = x[0] + + addEdge(selectedId!, pid, { + customId: `dep-${selectedId}-${pid}-${i}`, + type: 'dataflowedge' + }) + }) + } + } + + return { nodes, edges } +} diff --git a/frontend/src/lib/components/graph/index.ts b/frontend/src/lib/components/graph/index.ts index a1802f6a3c..7c0d23e403 100644 --- a/frontend/src/lib/components/graph/index.ts +++ b/frontend/src/lib/components/graph/index.ts @@ -1,4 +1,2 @@ -export { default as FlowGraph } from './FlowGraph.svelte' - export * from './model' export * from './util' diff --git a/frontend/src/lib/components/graph/model.ts b/frontend/src/lib/components/graph/model.ts index 939160bff5..e6b61d29db 100644 --- a/frontend/src/lib/components/graph/model.ts +++ b/frontend/src/lib/components/graph/model.ts @@ -1,18 +1,9 @@ import type { FlowStatusModule, Job } from '$lib/gen' import type { Writable } from 'svelte/store' -import type { UserNodeType } from './svelvet/types' import type { FlowState } from '../flows/flowState' export type ModuleHost = 'workspace' | 'inline' | 'hub' -export type Node = UserNodeType & { - parentIds: string[] - edgeLabel?: string - host?: ModuleHost - type: 'node' - loopDepth: number -} - export type Loop = { type: 'loop' items: NestedNodes diff --git a/frontend/src/lib/components/graph/renderers/edges/BaseEdge.svelte b/frontend/src/lib/components/graph/renderers/edges/BaseEdge.svelte new file mode 100644 index 0000000000..39bac15583 --- /dev/null +++ b/frontend/src/lib/components/graph/renderers/edges/BaseEdge.svelte @@ -0,0 +1,131 @@ + + + + {#if data?.insertable && !$useDataflow && !data?.moving} +
+ { + data?.eventHandlers.insert({ modules: data.modules, index: data.index, detail: e.detail }) + }} + bind:open={menuOpen} + /> +
+ {#if data.enableTrigger} +
+ { + data?.eventHandlers.insert({ + modules: data.modules, + index: data.index, + detail: e.detail + }) + }} + index={data?.index ?? 0} + modules={data?.modules ?? []} + /> +
+ {/if} + {/if} + + {#if data?.moving} +
+ {#if data.moving && !data.disableMoveIds?.includes(data.moving)} + + {/if} +
+ {/if} +
+ + + + diff --git a/frontend/src/lib/components/graph/renderers/edges/DataflowEdge.svelte b/frontend/src/lib/components/graph/renderers/edges/DataflowEdge.svelte new file mode 100644 index 0000000000..d44ce27271 --- /dev/null +++ b/frontend/src/lib/components/graph/renderers/edges/DataflowEdge.svelte @@ -0,0 +1,46 @@ + + + +
+ {data.sourceId} -> {data.targetId} +
+
+ + 100 + ? `${edgePath} ${getStraightLinePath({ sourceX, sourceY, targetY })}` + : edgePath} + {markerEnd} + style={`animation:dashdraw 0.5s linear infinite; stroke-dasharray: 5px;`} +/> diff --git a/frontend/src/lib/components/graph/renderers/edges/EmptyEdge.svelte b/frontend/src/lib/components/graph/renderers/edges/EmptyEdge.svelte new file mode 100644 index 0000000000..bc10f9d1f9 --- /dev/null +++ b/frontend/src/lib/components/graph/renderers/edges/EmptyEdge.svelte @@ -0,0 +1,29 @@ + + + diff --git a/frontend/src/lib/components/graph/renderers/nodes/BranchAllStart.svelte b/frontend/src/lib/components/graph/renderers/nodes/BranchAllStart.svelte new file mode 100644 index 0000000000..14b127e0a5 --- /dev/null +++ b/frontend/src/lib/components/graph/renderers/nodes/BranchAllStart.svelte @@ -0,0 +1,64 @@ + + + + { + data.eventHandlers.select(data.id) + }} + on:insert={(e) => { + data.eventHandlers.insert(e.detail) + }} + /> + {#if data.insertable} + + {/if} + diff --git a/frontend/src/lib/components/graph/renderers/nodes/BranchOneStart.svelte b/frontend/src/lib/components/graph/renderers/nodes/BranchOneStart.svelte new file mode 100644 index 0000000000..37f1270a0d --- /dev/null +++ b/frontend/src/lib/components/graph/renderers/nodes/BranchOneStart.svelte @@ -0,0 +1,61 @@ + + + + { + data.eventHandlers.select(data.id) + }} + /> + {#if data.insertable} + + {/if} + diff --git a/frontend/src/lib/components/graph/renderers/nodes/ForLoopEndNode.svelte b/frontend/src/lib/components/graph/renderers/nodes/ForLoopEndNode.svelte new file mode 100644 index 0000000000..d38367dbec --- /dev/null +++ b/frontend/src/lib/components/graph/renderers/nodes/ForLoopEndNode.svelte @@ -0,0 +1,29 @@ + + + + { + data?.eventHandlers?.select(e.detail) + }} + /> + diff --git a/frontend/src/lib/components/graph/renderers/nodes/ForLoopStartNode.svelte b/frontend/src/lib/components/graph/renderers/nodes/ForLoopStartNode.svelte new file mode 100644 index 0000000000..4623342dd4 --- /dev/null +++ b/frontend/src/lib/components/graph/renderers/nodes/ForLoopStartNode.svelte @@ -0,0 +1,31 @@ + + + + { + data?.eventHandlers?.select(e.detail) + }} + /> + diff --git a/frontend/src/lib/components/graph/renderers/nodes/InputNode.svelte b/frontend/src/lib/components/graph/renderers/nodes/InputNode.svelte new file mode 100644 index 0000000000..8b9c27c2ff --- /dev/null +++ b/frontend/src/lib/components/graph/renderers/nodes/InputNode.svelte @@ -0,0 +1,90 @@ + + + + {#if data.insertable && !data.disableAi} +
+ +
+ + + {#if !$copilotInfo.exists_openai_resource_path} +
+

+ Enable Windmill AI in the + { + close() + }} + > + workspace settings + + +

+
+ {/if} +
+
+ + AI Flow builder +
+
+ {/if} + { + data.eventHandlers?.insert(e.detail) + }} + on:select={(e) => { + data.eventHandlers?.select(e.detail) + }} + /> +
diff --git a/frontend/src/lib/components/graph/renderers/nodes/ModuleNode.svelte b/frontend/src/lib/components/graph/renderers/nodes/ModuleNode.svelte new file mode 100644 index 0000000000..f4df42336a --- /dev/null +++ b/frontend/src/lib/components/graph/renderers/nodes/ModuleNode.svelte @@ -0,0 +1,98 @@ + + + + = 0 ? selectedIteration : state?.flow_jobs?.length) + + '/' + + (state?.iteration_total ?? '?') + : ''} + bgColor={getStateColor(type, darkMode, '#fff')} + modules={data.modules ?? []} + moving={data.moving} + duration_ms={state?.duration_ms} + retries={data.retries} + {flowJobs} + on:delete={(e) => { + data.eventHandlers.delete(e.detail, '') + }} + on:insert={(e) => { + data.eventHandlers.insert(e.detail) + }} + on:move={(e) => { + data.eventHandlers.move(data.module, data.modules) + }} + on:newBranch={(e) => { + data.eventHandlers.newBranch(data.module) + }} + on:select={(e) => { + data.eventHandlers.select(e.detail) + }} + on:selectedIteration={(e) => { + selectedIteration = e.detail.index + 1 + data.eventHandlers.selectedIteration(e.detail, data.module.id) + }} + /> + +
+ {#if (data.value.type === 'branchall' || data.value.type === 'branchone') && data.insertable} + + {/if} +
+
diff --git a/frontend/src/lib/components/graph/renderers/nodes/NoBranchNode.svelte b/frontend/src/lib/components/graph/renderers/nodes/NoBranchNode.svelte new file mode 100644 index 0000000000..f60ad18751 --- /dev/null +++ b/frontend/src/lib/components/graph/renderers/nodes/NoBranchNode.svelte @@ -0,0 +1,33 @@ + + + + { + data?.eventHandlers?.select(e.detail) + }} + /> + diff --git a/frontend/src/lib/components/graph/renderers/nodes/NodeWrapper.svelte b/frontend/src/lib/components/graph/renderers/nodes/NodeWrapper.svelte new file mode 100644 index 0000000000..6bc6427a62 --- /dev/null +++ b/frontend/src/lib/components/graph/renderers/nodes/NodeWrapper.svelte @@ -0,0 +1,24 @@ + + + + +
+ +
+ +{#if enableSourceHandle} + +{/if} + +{#if enableTargetHandle} + +{/if} diff --git a/frontend/src/lib/components/graph/renderers/nodes/ResultNode.svelte b/frontend/src/lib/components/graph/renderers/nodes/ResultNode.svelte new file mode 100644 index 0000000000..5dbf32743b --- /dev/null +++ b/frontend/src/lib/components/graph/renderers/nodes/ResultNode.svelte @@ -0,0 +1,36 @@ + + + + { + data?.eventHandlers?.select(e.detail) + }} + /> + diff --git a/frontend/src/lib/components/graph/renderers/nodes/branchAllEndNode.svelte b/frontend/src/lib/components/graph/renderers/nodes/branchAllEndNode.svelte new file mode 100644 index 0000000000..d1c50554d0 --- /dev/null +++ b/frontend/src/lib/components/graph/renderers/nodes/branchAllEndNode.svelte @@ -0,0 +1,31 @@ + + + + { + data?.eventHandlers?.select(e.detail) + }} + /> + diff --git a/frontend/src/lib/components/graph/renderers/nodes/branchOneEndNode.svelte b/frontend/src/lib/components/graph/renderers/nodes/branchOneEndNode.svelte new file mode 100644 index 0000000000..ab446c74d3 --- /dev/null +++ b/frontend/src/lib/components/graph/renderers/nodes/branchOneEndNode.svelte @@ -0,0 +1,29 @@ + + + + { + data?.eventHandlers?.select(e.detail) + }} + /> + diff --git a/frontend/src/lib/components/graph/renderers/utils.ts b/frontend/src/lib/components/graph/renderers/utils.ts new file mode 100644 index 0000000000..8b393f1de9 --- /dev/null +++ b/frontend/src/lib/components/graph/renderers/utils.ts @@ -0,0 +1,32 @@ +import type { FlowStatusModule } from '$lib/gen' +import type { GraphModuleState } from '../model' + +export function getStraightLinePath({ sourceX, sourceY, targetY }) { + return `M${sourceX},${sourceY} L${sourceX},${targetY - 100}` +} + +export function computeBorderStatus( + branchIndex: number, + type: 'branchone' | 'branchall', + graphModuleState: GraphModuleState | undefined +): FlowStatusModule['type'] | undefined { + if (type === 'branchone') { + const branchChosen = graphModuleState?.branchChosen + + if (branchChosen === branchIndex) { + return graphModuleState?.type + } + } else { + let flow_jobs_success = graphModuleState?.flow_jobs_success + if (!flow_jobs_success) { + return 'WaitingForPriorSteps' + } else { + let status = flow_jobs_success?.[branchIndex] + if (status == undefined) { + return 'WaitingForExecutor' + } else { + return status ? 'Success' : 'Failure' + } + } + } +} diff --git a/frontend/src/lib/components/graph/svelvet/LICENSE b/frontend/src/lib/components/graph/svelvet/LICENSE deleted file mode 100644 index 73a46f09bf..0000000000 --- a/frontend/src/lib/components/graph/svelvet/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2022 OSLabs Beta - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/frontend/src/lib/components/graph/svelvet/container/README.md b/frontend/src/lib/components/graph/svelvet/container/README.md deleted file mode 100644 index 27a6c64fe7..0000000000 --- a/frontend/src/lib/components/graph/svelvet/container/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Container - -This folder contains two containers: Svelvet and Canvas. - -## Svelvet.svelte - -Svelvet.svelte is the top level component diff --git a/frontend/src/lib/components/graph/svelvet/container/controllers/middleware.ts b/frontend/src/lib/components/graph/svelvet/container/controllers/middleware.ts deleted file mode 100644 index bb3f8eb1cc..0000000000 --- a/frontend/src/lib/components/graph/svelvet/container/controllers/middleware.ts +++ /dev/null @@ -1,91 +0,0 @@ -/* -This file contains "middleware" functions that sanitize user input (UserNodeType and UserEdgeType). Put functions that -maintain consistency between previous - -*/ -import { bottomCb, leftCb, rightCb, topCb } from '../../edges/controllers/anchorCbUser' -import type { UserEdgeType, UserNodeType } from '../../types/types' - -/** - * sanitizeUserNodesAndEdges will sanitize the data initially passed in to Svelvet component. For example, the node that user specified have an integar as its id but to instantiate a Node and be compatible with uuid we will need to convert the integar id to a string. - * @param userNodes The array of nodes that have a UserNodeType - * @param userEdges The array of edges that have a UserEdgeType - * @returns An object of sanitized userNodes and userEdges - */ -export function sanitizeUserNodesAndEdges(userNodes: UserNodeType[], userEdges: UserEdgeType[]) { - convertIdToString(userNodes) - convertEdgeIdsToString(userEdges) - convertAnchorPositionsToCallbacks(userNodes, userEdges) - setDefaultEdgeType(userEdges) - return { userNodes, userEdges } -} - -/** - * setDefaultEdgeType ensures a default edge type of 'bezier' if the user does not set one, or sets a nonsense type. - * @param userEdges The array of edges that have a UserEdgeType - * @returns No return, the function edits the objects in place - */ -function setDefaultEdgeType(userEdges) { - for (let userEdge of userEdges) { - if (!['smoothstep', 'step', 'bezier', 'straight'].includes(userEdge.type)) - userEdge.type = 'bezier' - } -} - -/** - * convertAnchorPositionsToCallbacks - * @description - * WHY: This function is in order to maintain compliance with earlier versions of Svelvet. - * HISTORY: In Svelvet<=5, anchor points were hard-coded onto each node. Each node had a "sourcePosition" - * "targetPosition" where the edges would be attached. In Svelvet6, the store was re-designed - * from the ground up into an object-relational data model where anchor points could be attached - * at any point on the node using callbacks. This enabled features such as custom edge position, - * adaptive edge positioning, and dynamic edges. - * The purpose of this function is to parse the old way of specifying edge positions (as two source/target - * anchors on the node) into the Svelvet6 (where anchors are separate objects) in order to maintain a - * consistent user experience. However, we suggest that this functionality (parsing Svelvet5 syntax into - * Svelvet6 syntax) be removed completely in favor of only using Svelvet6 syntax in order to reduce edge cases. - */ -function convertAnchorPositionsToCallbacks(userNodes: UserNodeType[], userEdges: UserEdgeType[]) { - // convert userNodes array into object for constant time lookup by id - const userNodesObj: { [key: string]: UserNodeType } = {} - for (let userNode of userNodes) userNodesObj[userNode.id] = userNode - - // iterate through userEdges, and check the source/target nodes. - for (let userEdge of userEdges) { - const userNodeSource = userNodesObj[userEdge.source] - const userNodeTarget = userNodesObj[userEdge.target] - if (!userNodeSource || !userNodeTarget) continue - const sourcePosition = userNodeSource.sourcePosition - const targetPosition = userNodeTarget.targetPosition - - const cbs = { left: leftCb, right: rightCb, top: topCb, bottom: bottomCb } - if (sourcePosition) userEdge.sourceAnchorCb = cbs[sourcePosition] - if (targetPosition) userEdge.targetAnchorCb = cbs[targetPosition] - } -} - -/** - * Converts node id's to strings. For Svelvet<6, node id's were numbers. These were switched to strings for compatibility with uuid. This function does not return a new array, instead it mutates and sanitizes the original array. - * @param userNodes The array of userNodes (not yet sanitized) - */ -function convertIdToString(userNodes: UserNodeType[]) { - userNodes = userNodes.map((node) => { - node.id = node.id.toString() - node.childNodes = - node.childNodes === undefined ? [] : node.childNodes.map((childId) => childId.toString()) - return node - }) -} - -/** - * Converts source/target node id's to string. For Svelvet<6, id's were numbers. These were switched to strings for compatibility with uuid. This function does not return a new array, instead it mutates and sanitizes the original array. - * @param userEdges The array of userEdges (not yet sanitized) - */ -function convertEdgeIdsToString(userEdges: UserEdgeType[]) { - userEdges = userEdges.map((edge) => { - edge.source = edge.source.toString() - edge.target = edge.target.toString() - return edge - }) -} diff --git a/frontend/src/lib/components/graph/svelvet/container/models/index.ts b/frontend/src/lib/components/graph/svelvet/container/models/index.ts deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frontend/src/lib/components/graph/svelvet/container/views/GraphView.svelte b/frontend/src/lib/components/graph/svelvet/container/views/GraphView.svelte deleted file mode 100644 index 1a8de47950..0000000000 --- a/frontend/src/lib/components/graph/svelvet/container/views/GraphView.svelte +++ /dev/null @@ -1,304 +0,0 @@ - - -
- -
- -
- -
- {#if $copilotCurrentStepStore !== undefined} -
- {/if} - {#each nodes as node} - {#if node.data.html} - {@html node.data.html} - {:else if node.data.custom} - - node?.data?.custom?.cb?.('new', e.detail)} - on:delete={(e) => node?.data?.custom?.cb?.('delete', e.detail)} - on:select={(e) => node?.data?.custom?.cb?.('select', e.detail)} - on:insert={(e) => node?.data?.custom?.cb?.('insert', e.detail)} - on:newBranch={(e) => node?.data?.custom?.cb?.('newBranch', e.detail)} - on:deleteBranch={(e) => node?.data?.custom?.cb?.('deleteBranch', e.detail)} - on:move={(e) => node?.data?.custom?.cb?.('move', e.detail)} - on:nodeInsert={(e) => node?.data?.custom?.cb?.('nodeInsert', e.detail)} - on:addBranch={(e) => node?.data?.custom?.cb?.('addBranch', e.detail)} - on:removeBranch={(e) => node?.data?.custom?.cb?.('removeBranch', e.detail)} - on:selectedIteration={(e) => - node?.data?.custom?.cb?.('selectedIteration', e.detail)} - {...node.data.custom.props} - /> - - {:else} - {node.data.label} - {/if} - {/each} -
-
-
- - - - - - - - - - - - {#each edges as edge} - {#if edge.type === 'smoothstep'} - - {:else if edge.type === 'step'} - - {:else} - - {/if} - {/each} - - - - - - - -
-{#if showDataflowToggle} -
- -
-{/if} - -
- - - - {#if download} - - {/if} -
- - diff --git a/frontend/src/lib/components/graph/svelvet/container/views/Svelvet.svelte b/frontend/src/lib/components/graph/svelvet/container/views/Svelvet.svelte deleted file mode 100644 index 955226b9b2..0000000000 --- a/frontend/src/lib/components/graph/svelvet/container/views/Svelvet.svelte +++ /dev/null @@ -1,131 +0,0 @@ - - - -
- {#if error != ''} -
{error}
- {:else} - - {/if} -
- - diff --git a/frontend/src/lib/components/graph/svelvet/customCss/controllers/getCss.ts b/frontend/src/lib/components/graph/svelvet/customCss/controllers/getCss.ts deleted file mode 100644 index 16e68814cf..0000000000 --- a/frontend/src/lib/components/graph/svelvet/customCss/controllers/getCss.ts +++ /dev/null @@ -1,50 +0,0 @@ -// Required for "custom css" feature - -import type { NodeType, StoreType } from '../../store/types/types' - -// WHAT: For a given node with a user-defined classname, see if there are any css properties for height and width -// Get that height and width and set properties in node store. This is necessary for -export const forceCssHeightAndWidth = (store: StoreType, node: NodeType) => { - let width, height, innerText - // Look through each CSS rule to find the one the user defined - for (let i = 0; i < document.styleSheets.length; i++) { - const styleSheet = document.styleSheets[i] - const styleRules = styleSheet.cssRules - for (let j = 0; j < styleRules.length; j++) { - const rule = styleRules[j] - - // this is necessary to get rid of typescript warning for rule.selectorText - if (!(rule instanceof CSSStyleRule)) continue - - if (rule.selectorText === `.${node.className}`) { - const initialText = rule.cssText // getting the full text of the CSS rule - const i = initialText.indexOf('{') // finding index of first bracket - innerText = initialText.substring(i + 1, initialText.length - 1) // extracting the CSS to insert into inline style - // Adjusting the width and height if they are set via the custom class - const arr = innerText.split(' ') - arr.forEach((str, i) => { - if (str === 'width:') { - width = str.concat(arr[i + 1]) // go through the array and join width and the number - const w = parseInt(arr[i + 1]) // getting the number for the width - width = w - } - if (str === 'height:') { - height = str.concat(arr[i + 1]) // same as with the width - const h = parseInt(arr[i + 1]) - height = h - } - }) - } - } - } - - // update the width/height of the node - store.nodesStore.update((nodes) => { - if (width !== undefined) nodes[node.id].width = width - if (height !== undefined) nodes[node.id].height = height - return { ...nodes } - }) - - // move the node a bit to force update to anchors, potential anchors - node.setPositionFromMovement(0, 0) -} diff --git a/frontend/src/lib/components/graph/svelvet/d3/controllers/d3.ts b/frontend/src/lib/components/graph/svelvet/d3/controllers/d3.ts deleted file mode 100644 index 8eec38fccd..0000000000 --- a/frontend/src/lib/components/graph/svelvet/d3/controllers/d3.ts +++ /dev/null @@ -1,91 +0,0 @@ -// import { zoom, zoomTransform } from 'd3-zoom'; -// import { select, selectAll } from 'd3-selection'; -import type { ZoomBehavior } from 'd3-zoom'; -import { get } from 'svelte/store'; - -export function zoomInit( - d3, - canvasId, - d3Zoom, - d3Translate, - initialLocation, - initialZoom, - d3Scale -) { - //set default zoom logic - d3.select(`.Edges-${canvasId}`) - //makes sure translation is default at center coordinates - .transition() - .duration(0) - .call(d3Zoom.translateTo, 0, 0) - //moves camera to coordinates - .transition() - .duration(0) - .call( - d3Zoom.translateTo, - initialLocation.x / initialZoom, - initialLocation.y / initialZoom - ) - // zooms in on selected point - .transition() - .duration(0) - .call(d3Zoom.scaleTo, initialZoom.toFixed(2)); - // updates d3Translate with d3 object with x, y, and k values to be sent down to the minimap to be further calculated further - d3Translate = d3.zoomIdentity - .translate(initialLocation.x, initialLocation.y) - .scale(initialZoom.toFixed(2)); - d3.select(`.Nodes-${canvasId}`) - .transition() - .duration(0) - .call(d3Zoom.translateTo, 0, 0) - .transition() - .duration(0) - .call( - d3Zoom.translateTo, - initialLocation.x / initialZoom, - initialLocation.y / initialZoom - ) - .transition() - .duration(0) - .call(d3Zoom.scaleTo, initialZoom.toFixed(2)); - // sets D3 scale to current k of object - d3Scale.set(d3.zoomTransform(d3.select(`.Nodes-${canvasId}`)).k); - return d3Translate; -} -// create d3 instance conditionally based on boundary prop -export function determineD3Instance( - boundary, - d3: { zoom: () => ZoomBehavior }, - nodeSelected, - width, - height, - movementStore, - backgroundStore, - gridSize, - dotSize, - canvasId, - d3Scale, - handleZoom -): ZoomBehavior { - if (boundary) { - return d3 - .zoom() - .filter(() => !get(nodeSelected)) - .scaleExtent([0.4, 4]) // limits for zooming in/out - .translateExtent([ - [0, 0], - [boundary.x, boundary.y], - ]) // world extent - .extent([ - [0, 0], - [width, height], - ]) - .on('zoom', handleZoom); - } else { - return d3 - .zoom() - .filter(() => !get(nodeSelected)) - .scaleExtent([0.4, 2]) - .on('zoom', handleZoom); - } -} diff --git a/frontend/src/lib/components/graph/svelvet/edges/controllers/anchorCbDev.ts b/frontend/src/lib/components/graph/svelvet/edges/controllers/anchorCbDev.ts deleted file mode 100644 index 342698f25f..0000000000 --- a/frontend/src/lib/components/graph/svelvet/edges/controllers/anchorCbDev.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { rightCb, leftCb, topCb, bottomCb } from './anchorCbUser' // these are callbacks used to calculate anchor position relative to node -import type { AnchorCbType } from '../../edges/types/types' -import type { StoreType } from '../../store/types/types' - -import { getAnchors } from './util' -import { getNodeById } from '../../nodes/controllers/util' -export function fixedCbCreator( - store: StoreType, - edgeId: string, - anchorId: string, - userNodeId: string, - positionCb: Function // positionCb should be a function that takes 4 arguments (x,y,width,height) and returns a 3-array [x,y,angle] that represents the x,y position of the anchor as well as it's angle with respect to it's node. -) { - const rn = fixedCb - rn.type = 'fixed' - return rn - - function fixedCb() { - // get the two anchors - const anchors = getAnchors(store, { edgeId: edgeId }) - if (anchors.length !== 2) throw `there should be two anchors per edge, have: ${anchors.length}` - let [anchorSelf, anchorOther] = anchors - if (anchorSelf.id !== anchorId) [anchorSelf, anchorOther] = [anchorOther, anchorSelf] - - const node = getNodeById(store, userNodeId) - const { positionX, positionY, width, height } = node - const [x, y, angle] = positionCb(positionX, positionY, width, height) - anchorSelf.positionX = x - anchorSelf.positionY = y - anchorSelf.angle = angle - // update the other anchor if it is a dynamic anchor - // Note dyanamic anchor callbacks have a check that prevents an infinite loop - if (anchorOther.callback.type === 'dynamic') anchorOther.callback() - } -} - -export function dynamicCbCreator(store: StoreType, edgeId: string, anchorId: string) { - const rn = dynamicCb - rn.type = 'dynamic' - return rn - - function dynamicCb() { - // get the two anchors - const anchors = getAnchors(store, { edgeId: edgeId }) - if (anchors.length !== 2) throw `there should be two anchors per edge, have: ${anchors.length}` - - let [anchorSelf, anchorOther] = anchors - if (anchorSelf.id !== anchorId) [anchorSelf, anchorOther] = [anchorOther, anchorSelf] - // get the two nodes - const nodeSelf = getNodeById(store, anchorSelf.nodeId) - const nodeOther = getNodeById(store, anchorOther.nodeId) - // get the midpoints - const [xSelf, ySelf, xOther, yOther] = [ - nodeSelf.positionX + nodeSelf.width / 2, - nodeSelf.positionY + nodeSelf.height / 2, - nodeOther.positionX + nodeOther.width / 2, - nodeOther.positionY + nodeOther.height / 2 - ] - - // record angle for later. We use this so we don't have an infinite loop - let prevAngle = anchorSelf.angle - - // calculate the slope - const slope = (ySelf - yOther) / (xSelf - xOther) - // slope<1 means -45 to 45 degrees so left/right anchors - if (Math.abs(slope) < 1) { - // self node is on the left, other node is on the right - if (nodeSelf.positionX < nodeOther.positionX) { - const [selfX, selfY] = rightCb( - nodeSelf.positionX, - nodeSelf.positionY, - nodeSelf.width, - nodeSelf.height - ) - leftCb(nodeOther.positionX, nodeOther.positionY, nodeOther.width, nodeOther.height) - anchorSelf.setPosition(selfX, selfY) - anchorSelf.angle = 0 // if the self node is on the left, the anchor should have orientation of 0 degrees on the unit circle - } else { - // in this case, the self node is on the right and the other node is on the left - const [selfX, selfY] = leftCb( - nodeSelf.positionX, - nodeSelf.positionY, - nodeSelf.width, - nodeSelf.height - ) - rightCb(nodeOther.positionX, nodeOther.positionY, nodeOther.width, nodeOther.height) - anchorSelf.setPosition(selfX, selfY) - anchorSelf.angle = 180 - } - } else { - if (nodeSelf.positionY < nodeOther.positionY) { - // here the self node is above the other node - const [selfX, selfY] = bottomCb( - nodeSelf.positionX, - nodeSelf.positionY, - nodeSelf.width, - nodeSelf.height - ) - topCb(nodeOther.positionX, nodeOther.positionY, nodeOther.width, nodeOther.height) - anchorSelf.setPosition(selfX, selfY) - anchorSelf.angle = 270 - } else { - const [selfX, selfY] = topCb( - nodeSelf.positionX, - nodeSelf.positionY, - nodeSelf.width, - nodeSelf.height - ) - bottomCb(nodeOther.positionX, nodeOther.positionY, nodeOther.width, nodeOther.height) - anchorSelf.setPosition(selfX, selfY) - anchorSelf.angle = 90 - } - } - - // if the anchor changed position, then do operation for other anchor - // otherwise, don't do anything. This check is so we don't have an infinite loop - if (prevAngle !== anchorSelf.angle) anchorOther.callback() - } -} diff --git a/frontend/src/lib/components/graph/svelvet/edges/controllers/anchorCbUser.ts b/frontend/src/lib/components/graph/svelvet/edges/controllers/anchorCbUser.ts deleted file mode 100644 index 69e4d86bcd..0000000000 --- a/frontend/src/lib/components/graph/svelvet/edges/controllers/anchorCbUser.ts +++ /dev/null @@ -1,98 +0,0 @@ -/** - * These are callbacks used to define anchor positions relative to the node they are attached to. - * These may be provided to developers as examples of how to write their own custom callbacks for adjustable anchors - * It calculates the position of an anchor (x,y) coordinates given a node parameterized by (x,y,width, height) - */ - -/** - * @function rightCb - This is a callback function to define the anchor position on the node to be on the right side of the node. - * @param xNode - positionX of the attached node - * @param yNode - positionY of the attached node - * @param widthNode - width of the attached node - * @param heightNode - height of the attached node - * @returns [xAnchor, yAnchor, 0] - * xAnchor - positionX for the anchor - * yAnchor - positionY for the anchor - * 0 - this is angle of the anchor with respect to the node. The right anchor should fall at 0 degrees on the unit circle. - * @export rightCb - */ -export const rightCb = ( - xNode: number, - yNode: number, - widthNode: number, - heightNode: number -) => { - const xAnchor = xNode + widthNode; - const yAnchor = yNode + heightNode / 2; - return [xAnchor, yAnchor, 0]; -}; - -/** - * @function leftCb - This is a callback function to define the anchor position on the node to be on the left side of the node. - * @param xNode - positionX of the attached node - * @param yNode - positionY of the attached node - * @param widthNode - width of the attached node - * @param heightNode - height of the attached node - * @returns [xAnchor, yAnchor, 180] - * xAnchor - positionX for the anchor - * yAnchor - positionY for the anchor - * 180 - this is angle of the anchor with respect to the node. The left anchor should fall at 180 degrees on the unit circle. - * @export leftCb - */ -export const leftCb = ( - xNode: number, - yNode: number, - widthNode: number, - heightNode: number -) => { - const xAnchor = xNode; - const yAnchor = yNode + heightNode / 2; - return [xAnchor, yAnchor, 180]; -}; - -/** - * @function topCb - This is a callback function to define the anchor position on the node to be on the top of the node. - * @param xNode - positionX of the attached node - * @param yNode - positionY of the attached node - * @param widthNode - width of the attached node - * @param heightNode - height of the attached node - * @returns [xAnchor, yAnchor, 90] - * xAnchor - positionX for the anchor - * yAnchor - positionY for the anchor - * 90 - this is angle of the anchor with respect to the node. The top anchor should fall at 90 degrees on the unit circle. - * @export topCb - */ - -export const topCb = ( - xNode: number, - yNode: number, - widthNode: number, - heightNode: number -) => { - const xAnchor = xNode + widthNode / 2; - const yAnchor = yNode; - return [xAnchor, yAnchor, 90]; -}; - -/** - * @function bottomCb - This is a callback function to define the anchor position on the node to be on the bottom of the node. - * @param xNode - positionX of the attached node - * @param yNode - positionY of the attached node - * @param widthNode - width of the attached node - * @param heightNode - height of the attached node - * @returns - [xAnchor, yAnchor, 90] - * xAnchor - positionX for the anchor - * yAnchor - positionY for the anchor - * 270 - this is angle of the anchor with respect to the node. The bottom anchor should fall at 270 degrees on the unit circle. - * @export bottomCb - */ -export const bottomCb = ( - xNode: number, - yNode: number, - widthNode: number, - heightNode: number -) => { - const xAnchor = xNode + widthNode / 2; - const yAnchor = yNode + heightNode; - return [xAnchor, yAnchor, 270]; -}; diff --git a/frontend/src/lib/components/graph/svelvet/edges/controllers/util.ts b/frontend/src/lib/components/graph/svelvet/edges/controllers/util.ts deleted file mode 100644 index 7fdb1281da..0000000000 --- a/frontend/src/lib/components/graph/svelvet/edges/controllers/util.ts +++ /dev/null @@ -1,78 +0,0 @@ -import type { StoreType } from '../../store/types/types' -import { get } from 'svelte/store' -import type { AnchorType } from '../types/types' - -/** - * Finds all Anchors that matches the conditions specified in the filter parameter from a Svelvet store and returns these Anchors in an array. - * - * @param store The Svelvet store containing the state of a Svelvet component - * @param filter An object to specify conditions. Example: if filter = \{ sourceOrTarget: 'source', positionX: 35 \} then we will return all anchors with sourceOrTarget = source AND poxitionX = 35 - * @returns An array of Anchors that matches the conditions specified in the filter parameter - */ -export function getAnchors(store: StoreType, filter?: { [key: string]: any }) { - let anchors = Object.values(get(store.anchorsStore)) - // filter the array of anchors for elements that match filter - // Example: if filter = {sourceOrTarget: 'source', positionX: 35} then we will - //return all anchors with sourceOrTarget = source AND poxitionX = 35 - if (filter !== undefined) { - anchors = anchors.filter((anchor) => { - for (let filterKey in filter) { - const filterValue = filter[filterKey] - if (anchor[filterKey as keyof AnchorType] !== filterValue) return false - } - return true - }) - } - // return list of anchors - return anchors -} - -/** - * Gets one anchor (source anchor or target anchor) from a given edge - * - * @param store The Svelvet store containing the state of the Svelvet component - * @param edgeId The id of a given edge - * @param sourceOrTarget A string of 'source' or 'target' to specify which anchor the function should return - * @returns The source or target Anchor object of a given edge - */ -export function getAnchorFromEdge( - store: StoreType, - edgeId: string, - sourceOrTarget: 'source' | 'target' -): AnchorType { - getEdgeById(store, edgeId) - const anchors = getAnchors(store, { edgeId: edgeId }) - if (anchors.length !== 2) - throw `there should be two anchors for a given edge, there are ${anchors.length}` - // there should be one source anchor and one target anchor. Return the source anchor - const anchor = anchors.filter((anchor) => anchor.sourceOrTarget === sourceOrTarget) - if (anchor.length !== 1) throw `there should only be one source/target anchor` - return anchor[0] -} - -/** - * getEdgeById will look for the targeted Edge that has the same id provided in the Svelvet component store. - * - * @param store The Svelvet store containing the state of a Svelvet component - * @param id The id of the targeted Node - * @returns The targeted Edge object in store.edgesStore - */ -export function getEdgeById(store: StoreType, id: string) { - const edgesStore = get(store.edgesStore) - const edge = edgesStore[id] - if (edge === undefined) throw 'edge not found' - return edge -} - -/** - * getAnchorById will look for the targeted Anchor that has the same id in the Svelvet component store. - * - * @param store The Svelvet store containing the state of a Svelvet component - * @param id The id of the targeted Anchor - * @returns The target Anchor object in store.anchorsStore - */ -export function getAnchorById(store: StoreType, id: string) { - const anchorsStore = get(store.anchorsStore) - const anchor = anchorsStore[id] - return anchor -} diff --git a/frontend/src/lib/components/graph/svelvet/edges/models/Anchor.ts b/frontend/src/lib/components/graph/svelvet/edges/models/Anchor.ts deleted file mode 100644 index daa9b25b51..0000000000 --- a/frontend/src/lib/components/graph/svelvet/edges/models/Anchor.ts +++ /dev/null @@ -1,119 +0,0 @@ -/** - * This is where we create, update the Anchor store. - */ - -import type { AnchorCbType, AnchorType } from '../../edges/types/types'; -import { stores } from '../../store/models/store'; -import { getAnchors } from '../controllers/util'; - -/** Class representing an Anchor that implements Anchortype. - * @param {string} id The id of the Anchor - * @param {string} nodeId The id of the Node which the instantiated Anchor will be attached to - * @param {string} edgeId The id of the Edge which connects to the instantiated Anchor - * @param {'source' | 'target'} sourceOrTarget Specify the Anchor is a source or target - * @param {number} positionX The 'X' coordinate of the Anchor - * @param {number} positionY The 'Y' coordinate of the Anchor - * @param {function} callback The callback function that will determine the position of this Anchor - * @param {string} canvasId The canvasId of the Svelvet component that will hold this Anchor - * @param { number } angle This is the orientation of the anchor and is used to make sure bezier/step curves are rendered perpendicular to the node. Angles are defined along the unit circle. EX: 0 = right side of node, 180 = left side of node. - */ -export class Anchor implements AnchorType { - constructor( - public id: string, - public nodeId: string, - public edgeId: string, - public sourceOrTarget: 'source' | 'target', - public positionX: number, - public positionY: number, - public callback: AnchorCbType, - public canvasId: string, - public angle: 0 | 90 | 180 | 270 - ) {} - - getOtherAnchorId() { - const store = stores[this.canvasId]; - const anchors = getAnchors(store, { edgeId: this.edgeId }); - if (anchors.length !== 2) - throw 'something is wrong, every anchor should have exactly one other associated anchor'; - const anchor = anchors.filter((anchor) => anchor.id !== this.id)[0]; - return anchor.id; - } - - /** - * setPositionFromNode will invoke the user-defined callback to calculate the position of the Anchor, set the position of the Anchor in the anchorsStore, and also update the Edge position accordingly. - */ - setPositionFromNode() { - // calculate the new position of the anchor using user-defined callback - this.callback(); - const { edgesStore } = stores[this.canvasId]; - // update edges - edgesStore.update((edges) => { - const edge = edges[this.edgeId]; - if (this.sourceOrTarget === 'source') { - edge.sourceX = this.positionX; - edge.sourceY = this.positionY; - } else { - edge.targetX = this.positionX; - edge.targetY = this.positionY; - } - return { ...edges }; - }); - } - /** - * updateEdges will update the edgesStore based on Anchor's change. - */ - updateEdges() { - const { edgesStore } = stores[this.canvasId]; - /** update edges by deconstructing edges store and setting its new vlue to stores[this.canvasID] */ - - edgesStore.update((edges) => { - const edge = edges[this.edgeId]; - // this means that no edge was found, just return without doing anything - - if (edge === undefined) { - return { ...edges }; - } - if (this.sourceOrTarget === 'source') { - edge.sourceX = this.positionX; - edge.sourceY = this.positionY; - } else { - edge.targetX = this.positionX; - edge.targetY = this.positionY; - } - return { ...edges }; - }); - } - - /** - * setPosition will update the positionX and positionY of the Anchor and also cascade changes to related Edge. - * @param x The X coordinate of the new position for the Anchor - * @param y The Y coordinate of the new position for the Anchor - */ - setPosition(x: number, y: number) { - this.positionX = x; - this.positionY = y; - this.updateEdges(); - } - - /** - * Anchor.setPositionFromMovement works similarly to Anchor.setPosition. But setPosition is more powerful and we recommend using setPosition whenever possible and in the future, setPositionFromMovement can be removed. - * @param movementX The mouse movement value on the X-axis - * @param movementY The mouse movement value on the Y-axis - */ - setPositionFromMovement(movementX: number, movementY: number) { - this.positionX += movementX; - this.positionY += movementY; - const { edgesStore } = stores[this.canvasId]; - edgesStore.update((edges) => { - const edge = edges[this.edgeId]; - if (this.sourceOrTarget === 'source') { - edge.sourceX += movementX; - edge.sourceY += movementY; - } else { - edge.targetX += movementX; - edge.targetY += movementY; - } - return { ...edges }; - }); - } -} diff --git a/frontend/src/lib/components/graph/svelvet/edges/models/Edge.ts b/frontend/src/lib/components/graph/svelvet/edges/models/Edge.ts deleted file mode 100644 index f6f6ca8339..0000000000 --- a/frontend/src/lib/components/graph/svelvet/edges/models/Edge.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { findStore } from '../../store/models/store' -import type { UserEdgeType } from '../../types/types' -import type { EdgeType } from '../../store/types/types' - -import { stores } from '../../store/models/store' -import { getAnchors, getAnchorFromEdge } from '../../edges/controllers/util' -/** - * Class Edge that implements EdgeType. - * @param id The id of the Edge - * @param sourceX The X coordinate of the source Anchor - * @param sourceY The Y coordinate of the source Anchor - * @param targetX The X coordinate of the target Anchor - * @param targetY The Y coordinate of the target Anchor - * @param canvasId The canvasId of the Svelvet component that holds the instantiated Edge - * @param label The label of the Edge - * @param type The type of the Edge (options: 'straight', 'smoothstep', 'step', or 'bezier'). If user doesn't specify, the type will default to 'bezier'. - * @param labelBgColor The background color of the Edge label - * @param labelTextColor The text color of the Edge label - * @param edgeColor The color of the Edge - * @param animate Boolean value to specify whether the Edge should be animated - * @param noHandle Boolean value but looks like it is already depracated and can be removed without damage - * @param arraw Boolean value to specify whether the Edge displays an arrow near its target Anchor - */ -export class Edge implements EdgeType { - constructor( - public id: string, - public sourceX: number, - public sourceY: number, - public targetX: number, - public targetY: number, - public canvasId: string, - public label: string, - public type: 'straight' | 'smoothstep' | 'step' | 'bezier', - public labelBgColor: string, - public labelTextColor: string, - public edgeColor: string, - public animate: boolean, - public noHandle: boolean, - public arrow: boolean, - public clickCallback: Function, - public className: string, - public offset?: number - ) {} - - /** - * delete is going to delete the Edge and also delete associated Anchors - */ - delete() { - const store = stores[this.canvasId] - const { anchorsStore, edgesStore } = store - const sourceAnchor = getAnchorFromEdge(store, this.id, 'source') // this is a bit wasteful - const targetAnchor = getAnchorFromEdge(store, this.id, 'target') - anchorsStore.update((anchors) => { - for (const anchorId in anchors) { - if (anchorId === sourceAnchor.id || anchorId == targetAnchor.id) delete anchors[anchorId] - } - return { ...anchors } - }) - edgesStore.update((edges) => { - delete edges[this.id] - return { ...edges } - }) - } - - /** - * setExportableData will construct an object that holds all the edge data that can be exported. This is needed for the Exporting Diagram feature. - * @returns The object of exportable edge data. The format of the object should be as close as what user initially passes in to Svelvet. - */ - setExportableData() { - const exportableData: UserEdgeType = { - id: this.id, - label: this.label, - type: this.type, - labelBgColor: this.labelBgColor, - labelTextColor: this.labelTextColor, - edgeColor: this.edgeColor, - animate: this.animate, - noHandle: this.noHandle, - arrow: this.arrow, - source: 'dummy', // these will be set later - target: 'dummy' // these will be set later - } - - // set source, target on exportableData - const store = findStore(this.canvasId) - const anchors = getAnchors(store, { edgeId: this.id }) - if (anchors.length !== 2) throw 'there should be two anchors per edge' - for (const anchor of anchors) { - if (anchor.sourceOrTarget === 'target') exportableData.target = anchor.nodeId - if (anchor.sourceOrTarget === 'source') exportableData.source = anchor.nodeId - } - - return exportableData - } -} diff --git a/frontend/src/lib/components/graph/svelvet/edges/types/types.ts b/frontend/src/lib/components/graph/svelvet/edges/types/types.ts deleted file mode 100644 index 57917fc098..0000000000 --- a/frontend/src/lib/components/graph/svelvet/edges/types/types.ts +++ /dev/null @@ -1,19 +0,0 @@ -export type AnchorCbType = { - (): void; - type: 'dynamic' | 'fixed'; -}; - -export interface AnchorType { - id: string; // note that the user never specifies an anchor and they are generated dynamically. id will be a random string. - nodeId: string; - edgeId: string; - sourceOrTarget: 'source' | 'target'; - positionX: number; - positionY: number; - callback: AnchorCbType; // callback is used to calculate positionX, positionY based on parent node's data, and set the anchor position // TODO: rename to something better - angle: number; - setPositionFromNode: Function; - setPosition: Function; - updateEdges: Function; - getOtherAnchorId: Function; -} diff --git a/frontend/src/lib/components/graph/svelvet/edges/views/Edges/BaseEdge.svelte b/frontend/src/lib/components/graph/svelvet/edges/views/Edges/BaseEdge.svelte deleted file mode 100644 index 59b09bf80f..0000000000 --- a/frontend/src/lib/components/graph/svelvet/edges/views/Edges/BaseEdge.svelte +++ /dev/null @@ -1,108 +0,0 @@ - - - - - - - - - -{#if highlightEdgesOption} - {}} - /> -{/if} - -{#if arrow} - -{:else} - -{/if} - -{#if edgeTextProps.label} - -{/if} - - diff --git a/frontend/src/lib/components/graph/svelvet/edges/views/Edges/EdgeText.svelte b/frontend/src/lib/components/graph/svelvet/edges/views/Edges/EdgeText.svelte deleted file mode 100644 index 8e2021f6df..0000000000 --- a/frontend/src/lib/components/graph/svelvet/edges/views/Edges/EdgeText.svelte +++ /dev/null @@ -1,48 +0,0 @@ - - -{#if typeof label === 'undefined' || !label} - {null} -{:else} - - - - {label} - - -{/if} diff --git a/frontend/src/lib/components/graph/svelvet/edges/views/Edges/SimpleBezierEdge.svelte b/frontend/src/lib/components/graph/svelvet/edges/views/Edges/SimpleBezierEdge.svelte deleted file mode 100644 index e3c606ef8c..0000000000 --- a/frontend/src/lib/components/graph/svelvet/edges/views/Edges/SimpleBezierEdge.svelte +++ /dev/null @@ -1,175 +0,0 @@ - - - diff --git a/frontend/src/lib/components/graph/svelvet/edges/views/Edges/SmoothStepEdge.svelte b/frontend/src/lib/components/graph/svelvet/edges/views/Edges/SmoothStepEdge.svelte deleted file mode 100644 index 88ee91a754..0000000000 --- a/frontend/src/lib/components/graph/svelvet/edges/views/Edges/SmoothStepEdge.svelte +++ /dev/null @@ -1,157 +0,0 @@ - - - diff --git a/frontend/src/lib/components/graph/svelvet/edges/views/Edges/StepEdge.svelte b/frontend/src/lib/components/graph/svelvet/edges/views/Edges/StepEdge.svelte deleted file mode 100644 index 0e192fcd5f..0000000000 --- a/frontend/src/lib/components/graph/svelvet/edges/views/Edges/StepEdge.svelte +++ /dev/null @@ -1,8 +0,0 @@ - - - - diff --git a/frontend/src/lib/components/graph/svelvet/edges/views/Edges/types.ts b/frontend/src/lib/components/graph/svelvet/edges/views/Edges/types.ts deleted file mode 100644 index 48a97a8582..0000000000 --- a/frontend/src/lib/components/graph/svelvet/edges/views/Edges/types.ts +++ /dev/null @@ -1,58 +0,0 @@ -import type { XYPosition, Position } from './utils'; - -export interface Node { - id: number; - position: XYPosition; - data: T; - width: number; - height: number; - bgColor?: string; - fontSize?: number; - borderColor?: string; - borderRadius?: number; - textColor?: string; - clickCallback?: Function; - image?: boolean; - src?: string; - sourcePosition?: 'left' | 'right' | 'top' | 'bottom'; - targetPosition?: 'left' | 'right' | 'top' | 'bottom'; -} - -export interface Edge { - id: string; - source: number; - target: number; - label?: string; - labelBgColor?: string; - labelTextColor?: string; - edgeColor?: string; - type?: string; - animate?: boolean; - noHandle?: boolean; - arrow?: boolean; -} - -export interface DerivedEdge extends Edge { - sourceX: number; - sourceY: number; - sourcePosition: Position; - targetX: number; - targetY: number; - targetPosition: Position; -} - -export interface EdgeProps extends DerivedEdge { - path: string; - centerX: number; - centerY: number; -} - -export interface EdgeTextProps { - label?: any; - labelBgColor?: string; - labelTextColor?: string; - centerX: number; - centerY: number; -} - -export type HandleType = 'source' | 'target'; diff --git a/frontend/src/lib/components/graph/svelvet/edges/views/Edges/utils.ts b/frontend/src/lib/components/graph/svelvet/edges/views/Edges/utils.ts deleted file mode 100644 index 8685e6e3e8..0000000000 --- a/frontend/src/lib/components/graph/svelvet/edges/views/Edges/utils.ts +++ /dev/null @@ -1,84 +0,0 @@ -// enumerable values (static) set for Position -export enum Position { - Left = 'left', - Right = 'right', - Top = 'top', - Bottom = 'bottom', -} - -// interface for XYPosition to use in nodes and edges -export interface XYPosition { - x: number; - y: number; -} - -// type for z axis positioning with D3 -export type XYZPosition = XYPosition & { z: number }; - -// interface for changing dimensions of Viewport -export interface Dimensions { - width: number; - height: number; -} - -// interface of Rect divs in zoompane -export interface Rect extends Dimensions, XYPosition {} - -// interface of Box using XYPosition of nodes -export interface Box extends XYPosition { - x2: number; - y2: number; -} - -// D3 type array for Transform -export type Transform = [number, number, number]; - -// -// export type CoordinateExtent = [[number, number], [number, number]]; - -export interface GetCenterParams { - sourceX: number; - sourceY: number; - targetX: number; - targetY: number; - sourcePosition?: Position; - targetPosition?: Position; -} -//needed for getCenter funciotn -const LeftOrRight = [Position.Left, Position.Right]; -//used to determine the position for edge text on a Smooth or Step Edge -export const getCenter = ({ - sourceX, - sourceY, - targetX, - targetY, - sourcePosition = Position.Bottom, - targetPosition = Position.Top, -}: GetCenterParams): [number, number, number, number] => { - const sourceIsLeftOrRight = LeftOrRight.includes(sourcePosition); - const targetIsLeftOrRight = LeftOrRight.includes(targetPosition); - - // we expect flows to be horizontal or vertical (all handles left or right respectively top or bottom) - // a mixed edge is when one the source is on the left and the target is on the top for example. - const mixedEdge = - (sourceIsLeftOrRight && !targetIsLeftOrRight) || - (targetIsLeftOrRight && !sourceIsLeftOrRight); - - if (mixedEdge) { - const xOffset = sourceIsLeftOrRight ? Math.abs(targetX - sourceX) : 0; - const centerX = sourceX > targetX ? sourceX - xOffset : sourceX + xOffset; - - const yOffset = sourceIsLeftOrRight ? 0 : Math.abs(targetY - sourceY); - const centerY = sourceY < targetY ? sourceY + yOffset : sourceY - yOffset; - - return [centerX, centerY, xOffset, yOffset]; - } - - const xOffset = Math.abs(targetX - sourceX) / 2; - const centerX = targetX < sourceX ? targetX + xOffset : targetX - xOffset; - - const yOffset = Math.abs(targetY - sourceY) / 2; - const centerY = targetY < sourceY ? targetY + yOffset : targetY - yOffset; - - return [centerX, centerY, xOffset, yOffset]; -}; diff --git a/frontend/src/lib/components/graph/svelvet/nodes/controllers/util.ts b/frontend/src/lib/components/graph/svelvet/nodes/controllers/util.ts deleted file mode 100644 index f3aff1d3b3..0000000000 --- a/frontend/src/lib/components/graph/svelvet/nodes/controllers/util.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { StoreType } from '../../store/types/types'; -import { get } from 'svelte/store'; - -/** - * getNodeById will look for the targeted Node that has the same id provided in the Svelvet component store. - * - * @param store The Svelvet store containing the state of a Svelvet component - * @param id The id of the targeted Node - * @returns The targeted Node object in store.nodesStore - */ - -export function getNodeById(store: StoreType, id: string) { - const nodesStore = get(store.nodesStore); - const node = nodesStore[id]; - return node; -} diff --git a/frontend/src/lib/components/graph/svelvet/nodes/models/Node.ts b/frontend/src/lib/components/graph/svelvet/nodes/models/Node.ts deleted file mode 100644 index cbadfde187..0000000000 --- a/frontend/src/lib/components/graph/svelvet/nodes/models/Node.ts +++ /dev/null @@ -1,156 +0,0 @@ -/** this is where we create our node store */ -import type { NodeType } from '../../store/types/types' -import { get } from 'svelte/store' -import { stores } from '../../store/models/store' -/** A Node class that implements NodeType interface - * @param {string} id The id of the Node - * @param {number} positionX The X-axis position of the Node (left top corner of the Node) - * @param {number} positionY The Y-axis position of the Node (left top corner of the Node) - * @param {number} width The width of the Node - * @param {number} height The height of the Node - * @param {string} bgColor The background color of the node - * @param {object} data A data object that user can specify; possible keys are 'label' and 'custom'; - * @param {string} canvasId The canvasId of the Svelvet component that the instantiated Node will be on. - * @param {string} borderColor The border color of the Node - * @param {boolean} image A boolean set to true if the Node needs to display an image - * @param {string} src The src link for the image; image and src are closely tied and a src link is only needed when image sets to true - * @param {string} textColor The color of the text in the Node - * @param {string} borderRadius The border radius of the Node - * @param {string} childNodes An array of node ids that will be grouped as child nodes of this Node. This is for the GroupNodes feature. The current implementation of this feature works one way but not the other (when you drag the parent node, the child nodes will move as a group but when you drag the child node, the parent node would not move along) - * @param {string} className The custom class name if user specifies. This is for the custom className feature for Node. - */ -export class Node implements NodeType { - constructor( - public id: string, - public positionX: number, - public positionY: number, - public width: number, - public height: number, - public bgColor: string, - public data: object, - public canvasId: string, - public borderColor: string, - public image: boolean, - public src: string, - public textColor: string, - public borderRadius: number, - public childNodes: string[], - public className: string, - public clickCallback: Function - ) {} - - /** - * setPositionFromMovement will update the positionX and positionY of the Node when user drags a Node around on the canvas, reflect the changes in real time in the nodesStore, and also cascade the changes to all relative elements like Anchors and Edges. - * @param {number} movementX The mouse movement value on the X-axis - * @param {number} movementY The mouse movement value on the Y-axis - */ - setPositionFromMovement(movementX: number, movementY: number) { - const { nodesStore, anchorsStore, potentialAnchorsStore, boundary, lockedOption } = - stores[this.canvasId] - - if (get(lockedOption)) return // don't do anything if locked is enabled - - // boundary sets the boundary of the canvas, or else it is false if their is no boundary - // check if out of bounds, and if so return without doing anything - const boundaryObj = get(boundary) - if ( - typeof boundaryObj === 'object' && - (this.positionX + this.width + movementX >= boundaryObj.x || - this.positionY + this.height + movementY >= boundaryObj.y || - this.positionY + movementY <= 0 || - this.positionX + movementX <= 0) - ) - return - //update all necessary data - this.positionX += movementX - this.positionY += movementY - - // update children - nodesStore.update((nodes) => { - if (this.childNodes) - for (const childNodeId of this.childNodes) - nodes[childNodeId].setPositionFromMovement(movementX, movementY) - return { ...nodes } - }) - - //update all the anchors on the node in the anchorsStore - anchorsStore.update((anchors) => { - for (const anchorId in anchors) { - if (anchors[anchorId].nodeId === this.id) { - anchors[anchorId].setPositionFromNode() - } - } - return { ...anchors } - }) - - //update all the anchors on the node in the anchorsStore - potentialAnchorsStore.update((anchors) => { - for (const anchorId in anchors) { - if (anchors[anchorId].nodeId === this.id) { - anchors[anchorId].callback() // we don't have to worry about setting partner anchors/etc; - } - } - return { ...anchors } - }) - } - - /** - * setSizeFromMovement will update the width and height of the Node when user resizes the Node by dragging at the right bottom corner (where the ResizedNode attached), reflect the changes in real time in the nodesStore, and also cascade the changes to all relative elements like Anchors and potential Anchors. - * - * @param movementX The mouse movement value on the X-axis - * @param movementY The mouse movement value on the Y-axis - */ - setSizeFromMovement(movementX: number, movementY: number) { - this.width += movementX - this.height += movementY - - const { anchorsStore, potentialAnchorsStore } = stores[this.canvasId] - - //Updates the anchor so it follows the node's position as the dimensions change - anchorsStore.update((anchors) => { - for (const anchorId in anchors) { - if (anchors[anchorId].nodeId === this.id) { - anchors[anchorId].setPositionFromNode() - //anchors[anchorId].setPosition(movementX, movementY); - } - } - return { ...anchors } - }) - - //update all the anchors on the node in the anchorsStore - potentialAnchorsStore.update((anchors) => { - for (const anchorId in anchors) { - if (anchors[anchorId].nodeId === this.id) { - anchors[anchorId].callback() // we don't have to worry about setting partner anchors/etc; - } - } - return { ...anchors } - }) - } - - /** - * setExportableData is going to construct an object that holds all the node data that can be exported. This method is used for Exporting Diagrams feature. - * - * @returns An object with all the exportable data of the Node. The format of this object should align with the original format of node data user provided. - */ - setExportableData() { - const exportableData = { - id: this.id, - // canvasId: this.canvasId, - width: this.width, - height: this.height, - position: { x: this.positionX, y: this.positionY }, - data: this.data, - bgColor: this.bgColor, - borderColor: this.borderColor, - textColor: this.textColor, - borderRadius: this.borderRadius, - image: this.image, - src: this.src, - childNodes: this.childNodes, - customClassName: this.className - } - - return exportableData - } -} diff --git a/frontend/src/lib/components/graph/svelvet/nodes/views/Node.svelte b/frontend/src/lib/components/graph/svelvet/nodes/views/Node.svelte deleted file mode 100644 index f409fe9d26..0000000000 --- a/frontend/src/lib/components/graph/svelvet/nodes/views/Node.svelte +++ /dev/null @@ -1,91 +0,0 @@ - - - -
- - {#if node.image} - - {/if} - -
- - diff --git a/frontend/src/lib/components/graph/svelvet/store/controllers/storeApi.ts b/frontend/src/lib/components/graph/svelvet/store/controllers/storeApi.ts deleted file mode 100644 index 3cbb7ffe93..0000000000 --- a/frontend/src/lib/components/graph/svelvet/store/controllers/storeApi.ts +++ /dev/null @@ -1,103 +0,0 @@ -/* - - -Important functions (in order of how likely I think you are to use them): - -findStore(canvasId: string) -- Input: canvasId. There can be multiple Svelvet canvases on the same page, and each has their own store -- Returns: store -- Notes: You will need to call this function on every component you make - -getNodes(store, filter) -- Description: this function returns of list of Node objects -- Input: store: the store where you get the list of Node objects from -- filter: an object where you can filter the array. For example, if you want to find nodes with an id of 'sdf-2d3fs' and positionX=35, then you - would set filter = {id: 'sdf-2d3fs, positionX: 35}. - Right now, you can only test for equality. -- Notes: This is a very mongoose way of retrieving information. Given the relational nature of our stores, maybe it would be better to use an SQL-like syntax - This array is non-reactive (ie, you cannot use information from this array to force a re-render of a Svelte component) - -createStoreEmpty(canvasId: string) -- Input: canvasId. -- Returns: store -- Notes. This should be called once every time you initialize a new Svelvet canvas, (ie, only in the Svelvet.svelte file) - -populateSvelvetStoreFromUserInput(canvasId, nodes, edges) -- canvasId: this the the canvasId of the Svelvet component you are creating a store for -- nodes: this is an array of objects containing node info that is defined by the user. NOTE THAT THE STRUCTURE DIFFERS FROM THE NODES CLASS - The whole point of populateSvelvetStoreFromUserInput is to convert nodes into proper Svelvet Node objects. An example of nodes is in - $routes/testingplayground/index.svelte -- edges: same as nodes, this is an array of objects containing edge info THAT IS DIFFERENT FROM THE EDGE CLASS. -- Returns: store -*/ -import { findStore, stores } from '../models/store' -import { writable } from 'svelte/store' - -import type { StoreType } from '../types/types' -import type { UserNodeType, UserEdgeType } from '../../types/types' -import { populateAnchorsStore, populateNodesStore, populateEdgesStore } from './util' - - -/** - * createStoreEmpty will initialize a new Svelvet store with a unique canvasId. - * If you have multiple Svelvet components on the page, the stores object will look like the following example: - * const stores = \{ - * canvasId-1: store of Svelvet component 1, - * canvasId-2: store of Svelvet component 2, - * canvasId-3: store of Svelvet component 3, - * \} - * Notes: This should be called once every time you initialize a new Svelvet canvas, (ie, only in the Svelvet.svelte file). - * This function will initialize an empty store for the Svelvet component and should be followed by invoking populateSvelvetStoreFromUserInput to populate all the initial state from the user input. - * - * @param canvasId The canvasId of the newly created Svelvet component - * @returns An empty store for the newly created Svelvet component. - */ -export function createStoreEmpty(canvasId: string): StoreType { - stores[canvasId] = { - nodesStore: writable({}), - edgesStore: writable({}), - anchorsStore: writable({}), - potentialAnchorsStore: writable({}), - widthStore: writable(600), - heightStore: writable(600), - backgroundStore: writable(false), - movementStore: writable(true), - nodeSelected: writable(false), - nodeIdSelected: writable(-1), - d3Scale: writable(1), - options: writable({}), - temporaryEdgeStore: writable([]), - nodeCreate: writable(false), // this option sets whether the "nodeEdit" feature is enabled - boundary: writable(false), - edgeEditModal: writable(null), // this is used for edgeEditModal feature. When an edge is right clicked, store.edgeEditModal is set to the edgeId string. This causes a modal to be rendered - lockedOption: writable(false), - editableOption: writable(false), // true if you want nodes/edges to be editable. See feature editEdges - d3ZoomParameters: writable({}), // this stores d3 parameters x, y, and zoom. This isn't used for anything other than giving users a way to access d3 zoom parameters if they want to build on top of Svelvet - highlightEdgesOption: writable(true) // option to turn on/off highlightable edges - } - return stores[canvasId] -} - -/** - * populateSvelvetStoreFromUserInput will populate all the states and set these states into the Svelvet store initialized by invoking createStoreEmpty - * - * @param canvasId The canvasId of the Svelvet component you are creating a store for - * @param nodes This is an array of objects containing node info that is defined by the user. NOTE THAT THE STRUCTURE DIFFERS FROM THE NODES CLASS. The whole point of populateSvelvetStoreFromUserInput is to convert nodes into proper Svelvet Node objects. An example of nodes is in $routes/testingplayground/index.svelte - * @param edges Same as nodes, this is an array of objects containing edge info THAT IS DIFFERENT FROM THE EDGE CLASS. - */ -export function populateSvelvetStoreFromUserInput( - canvasId: string, - nodes: UserNodeType[], - edges: UserEdgeType[] -): void { - // find the store - const store = findStore(canvasId) - - // populate store.nodesStore with user nodes - populateNodesStore(store, nodes, canvasId) - // populate store.anchorsStore with anchors. Note the userdoes not explictly define anchors; anchors are calculated from the edges - populateAnchorsStore(store, nodes, edges, canvasId) - // populate edges - populateEdgesStore(store, edges, canvasId) - -} diff --git a/frontend/src/lib/components/graph/svelvet/store/controllers/userApi.ts b/frontend/src/lib/components/graph/svelvet/store/controllers/userApi.ts deleted file mode 100644 index edeec17454..0000000000 --- a/frontend/src/lib/components/graph/svelvet/store/controllers/userApi.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { findStore } from '../models/store'; -import { get } from 'svelte/store'; -export function getD3PositionX(canvasId) { - const store = findStore(canvasId); - const width = get(store.widthStore); - const x = width / 2 - get(store.d3ZoomParameters).x; // user input is shifted so that x=0, y=0 occurs in the center - return x; -} -export function getD3PositionY(canvasId) { - const store = findStore(canvasId); - const height = get(store.heightStore); - const y = height / 2 - get(store.d3ZoomParameters).y; // user input is shifted so that x=0, y=0 occurs in the center - return y; -} -export function getD3Zoom(canvasId) { - const store = findStore(canvasId); - return get(store.d3ZoomParameters).k; -} diff --git a/frontend/src/lib/components/graph/svelvet/store/controllers/util.ts b/frontend/src/lib/components/graph/svelvet/store/controllers/util.ts deleted file mode 100644 index bcd42fe9a1..0000000000 --- a/frontend/src/lib/components/graph/svelvet/store/controllers/util.ts +++ /dev/null @@ -1,215 +0,0 @@ -const pkStringGenerator = () => (Math.random() + 1).toString(36).substring(7) // these are callbacks used to calculate anchor position relative to node -import { dynamicCbCreator, fixedCbCreator } from '../../edges/controllers/anchorCbDev' - -import type { AnchorCbType, AnchorType } from '../../edges/types/types' - -import type { UserEdgeType, UserNodeType } from '../../types/types' -import type { EdgeType, NodeType, StoreType } from '../types/types' - -import { getAnchors } from '../../edges/controllers/util' -import { Anchor } from '../../edges/models/Anchor' -import { Edge } from '../../edges/models/Edge' -import { Node } from '../../nodes/models/Node' - -/** - * Creates an Anchor on the targeted Node with infomation the userNode holds - * @param store An object containing the state of the Svelvet component. You can access the following through `store`: nodesStore, edgesStore, anchorsStore, etc. - * @param userNode A node that the user specifies. This is NOT the same as a Node object. - * @param sourceOrTarget User specified information of source or target - * @param canvasId The id of the canvas that holds the Anchor and its attached Node - * @param edge An edge that the user specifies. It should target the userNode as source or target. This is NOT the same as an Edge object - * @returns An Anchor object - */ - -function createAnchor( - store: StoreType, - userNode: UserNodeType | null, - sourceOrTarget: 'source' | 'target', - canvasId: string, - edge: UserEdgeType -) { - // edge case - if (userNode === null) throw `you cannot create an anchor without a user node (for now)` - - const edgeId = edge.id - const anchorId = pkStringGenerator() - - // userCb is the appropriate source or taret callback from userEdge object. It is - // possible the user to NOT set userCb in which case userCb will be undefined - let userCb: Function | undefined - if (sourceOrTarget === 'target') userCb = edge.targetAnchorCb - else userCb = edge.sourceAnchorCb - - // create anchor callbacks - - let cb: AnchorCbType - if (userCb === undefined) cb = dynamicCbCreator(store, edgeId, anchorId) - else cb = fixedCbCreator(store, edgeId, anchorId, userNode.id, userCb) - - // Create a new anchor. - const anchor = new Anchor( - anchorId, - userNode.id, - edgeId, - sourceOrTarget, - -1, // dummy variables for x,y,angle for now - -1, // dummy variables for x,y,angle for now - cb, - canvasId, - 0 // dummy variables for x,y,angle for now - ) - // return - return anchor -} - -/** - * Populates edgesStore of Edges. This function does not return the edgesStore. Instead it sets the nodesStore of Svelvet store. - * @param store An object containing the state of the Svelvet component. You can access the following through `store`: nodesStore, edgesStore, anchorsStore, etc. - * @param edges An edge that the user specifies. This is NOT the same as a Edge object. - * @param canvasId The canvasId of the Svelvet component that holds the Edges - */ -export function populateEdgesStore(store: StoreType, edges: UserEdgeType[], canvasId: string) { - const edgesStore: { [key: string]: EdgeType } = {} - for (let i = 0; i < edges.length; i++) { - const userEdge = edges[i] - // { id: 'e1-2', source: 1, type: 'straight', target: 2, label: 'e1-2' }, - // source is node.id for the source node - // target is node.id for the target node - // We need to get the anchors - const { id: edgeId } = userEdge - - const anchors = getAnchors(store, { edgeId: edgeId }) - // check that we have two anchors for every edge - if (anchors.length !== 2) throw 'We should have two anchors for every node' - // check that we have 1 source anchor and 1 target anchor. Since sourceOrTarget is typed to be either 'source' - // or 'target', it suffices to check whether there are two unique elements - if (new Set(anchors.map((e) => e.sourceOrTarget)).size !== 2) - throw 'we should have one source and one target anchor' - // get source and target anchor - let sourceAnchor, targetAnchor - if (anchors[0].sourceOrTarget === 'source') { - sourceAnchor = anchors[0] - targetAnchor = anchors[1] - } else { - sourceAnchor = anchors[1] - targetAnchor = anchors[0] - } - - edgesStore[edgeId] = new Edge( - edgeId, - sourceAnchor.positionX, - sourceAnchor.positionY, - targetAnchor.positionX, - targetAnchor.positionY, - canvasId, - userEdge.label === undefined ? '' : userEdge.label, - userEdge.type === undefined ? 'bezier' : userEdge.type, - userEdge.labelBgColor === undefined ? 'white' : userEdge.labelBgColor, - userEdge.labelTextColor === undefined ? 'black' : userEdge.labelTextColor, - userEdge.edgeColor === undefined ? 'black' : userEdge.edgeColor, - userEdge.animate === undefined ? false : userEdge.animate, - userEdge.noHandle === undefined ? false : userEdge.noHandle, - userEdge.arrow === undefined ? false : userEdge.arrow, - userEdge.clickCallback === undefined ? () => {} : userEdge.clickCallback, - userEdge.className === undefined ? '' : userEdge.className, - userEdge.offset - ) - } - store.edgesStore.set(edgesStore) -} - -/** - * Finds userNode (with UserNodeType; Not the same as the Node) by the node id from nodesStore - * @param id The id of the Node in its nodesStore - * @param userNodes The array of userNodes (NOT the same as Node object) - * @returns The node that user specified or null if not found - */ -function findUserNodeById(id: string, userNodes: UserNodeType[]): UserNodeType | null { - for (let i = 0; i < userNodes.length; i++) { - const userNode = userNodes[i] - if (userNode.id === id) return userNode - } - return null -} - -/** - * Populates the anchorsStore. This will overwrite any data in the AnchorsStore. - * @param store The Svelvet store containing the state of the Svelvet component - * @param nodes An array of user specified nodes - * @param edges An array of user specified edges - * @param canvasId The canvasId of the Svelvet component that holds the nodes and edges - */ -export function populateAnchorsStore( - store: StoreType, - nodes: UserNodeType[], - edges: UserEdgeType[], - canvasId: string -) { - // anchorsStore will populated and eventaully synchronized to store.anchorsStore - const anchorsStore: { [key: string]: AnchorType } = {} - // iterate through user edges. Note the user never explicitly defines anchors; we calculate anchors - // from the user edge/node information - for (let i = 0; i < edges.length; i++) { - const userEdge = edges[i] - // find the source and target userNodes. These will be used to create the nodeId foreign key and - // determine placement of the anchor based on userNode.targetPosition, useNode.sourcePosition - const { source: sourceNodeId, target: targetNodeId } = userEdge - const sourceUserNode = findUserNodeById(sourceNodeId, nodes) - const targetUserNode = findUserNodeById(targetNodeId, nodes) - // create source anchor - const sourceAnchor = createAnchor(store, sourceUserNode, 'source', canvasId, userEdge) - // create target anchor - const targetAnchor = createAnchor(store, targetUserNode, 'target', canvasId, userEdge) - // store source and target anchors - anchorsStore[sourceAnchor.id] = sourceAnchor - anchorsStore[targetAnchor.id] = targetAnchor - } - - //populates the anchorsStore - store.anchorsStore.set(anchorsStore) - - // set anchor positions. We can only set anchor positions after anchorsStore and nodesStore - // has been populated. TODO: maybe add a check to see that anchorsStore and NodesStore populated? - const anchors = getAnchors(store) - for (const anchor of anchors) anchor.callback() -} - -/** - * Populates the nodesStore. This will overwrite any data in the nodesStore. - * @param store The Svelvet store containing the state of the Svelvet component - * @param nodes An array of user specifed nodes - * @param canvasId The canvasId of the Svelvet component that holds the nodes - */ -export function populateNodesStore(store: StoreType, nodes: UserNodeType[], canvasId: string) { - // this is the nodesStore object. THIS IS NOT THE SAME AS A NODESTORE - const nodesStore: { [key: string]: NodeType } = {} - // iterate through user nodes and create node objects - for (let i = 0; i < nodes.length; i++) { - const userNode: UserNodeType = nodes[i] - const nodeId = userNode.id - - // TODO: move sanitizing default values to middleware - const node = new Node( - nodeId.toString(), - userNode.position.x, - userNode.position.y, - userNode.width, - userNode.height, - userNode.bgColor ?? 'white', - userNode.data, - canvasId, - userNode.borderColor === undefined ? 'black' : userNode.borderColor, - userNode.image === undefined ? false : userNode.image, - userNode.src === undefined ? '' : userNode.src, - userNode.textColor === undefined ? '' : userNode.textColor, - userNode.borderRadius === undefined ? 0 : userNode.borderRadius, - userNode.childNodes === undefined ? [] : userNode.childNodes, - userNode.className === undefined ? '' : userNode.className, - userNode.clickCallback === undefined ? () => {} : userNode.clickCallback - ) - - nodesStore[nodeId] = node - } - // This is actually what sets the store - store.nodesStore.set(nodesStore) -} diff --git a/frontend/src/lib/components/graph/svelvet/store/models/store.ts b/frontend/src/lib/components/graph/svelvet/store/models/store.ts deleted file mode 100644 index 0765623a9a..0000000000 --- a/frontend/src/lib/components/graph/svelvet/store/models/store.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { StoreType } from '../types/types'; - - -export function findStore(canvasId: string): StoreType { - return stores[canvasId] -} -/** - `store` is a dictionary of Svelvet stores. - * The reason why we have multiple Svelvet stores is to handle multiple canvases on the same page. - * A Svelvet store is the single source of truth for a canvas state. - * We discourage developers from interacting with stores directly; instead use the api methods in - `src/lib/controllers/storeApi.ts`. However, if need to direct access you can do so by importing: - `import { store } from 'src/lib/models/store';` -*/ -export const stores: { [key: string]: StoreType } = {}; diff --git a/frontend/src/lib/components/graph/svelvet/store/types/types.ts b/frontend/src/lib/components/graph/svelvet/store/types/types.ts deleted file mode 100644 index 8cd17b95a6..0000000000 --- a/frontend/src/lib/components/graph/svelvet/store/types/types.ts +++ /dev/null @@ -1,122 +0,0 @@ -import type { Writable } from 'svelte/store' -import type { AnchorType } from '../../edges/types/types' - -export interface ResizeNodeType { - id: string - nodeId: string - edgeId?: string - canvasId: string - anchorId?: string - positionX: number - positionY: number - setPositionAndCascade: Function - setPosition: Function - delete: Function -} - -/* -Type for a single svelvet store -*/ -export interface StoreType { - nodesStore: Writable<{ [key: string]: NodeType }> - edgesStore: Writable<{ [key: string]: EdgeType }> - anchorsStore: Writable<{ [key: string]: AnchorType }> - potentialAnchorsStore: Writable<{ [key: string]: PotentialAnchorType }> - widthStore: Writable - heightStore: Writable - backgroundStore: Writable - movementStore: Writable - nodeIdSelected: Writable - nodeSelected: Writable // this is used to stop d3 panning when node is being dragged - d3Scale: Writable // for zoom and pan - options: Writable<{ [key: string]: any }> - temporaryEdgeStore: Writable - nodeCreate: Writable // this option sets whether the "nodeEdit" feature is enabled - boundary: Writable - edgeEditModal: Writable // this options is used to place the edgeEdit modal when an edge is right-clicked. null is no modal, positionType if modal should be placed at position defined by postionType.x, positionType.y - lockedOption: Writable - editableOption: Writable - d3ZoomParameters: Writable<{ - [key: string]: number - }> - highlightEdgesOption: Writable -} - -export interface PositionType { - x: number - y: number -} - -export interface NodeType { - id: string - width: number - height: number - positionX: number - positionY: number - bgColor: string - data: { - html?: any - custom?: { component: any; props?: any; cb?: (e: string, detail: any) => void } - img?: any - label?: string - } - canvasId: string - setPositionFromMovement: Function - setSizeFromMovement: Function - setExportableData: Function - borderColor: string - image: boolean - src: string - textColor: string - borderRadius: number - childNodes: string[] - className: string //This is for custom className for node - clickCallback: Function // user-supplied callback that executes when the node is clicked -} - -export interface EdgeType { - id: string - sourceX: number - sourceY: number - targetX: number - targetY: number - canvasId: string - label: string - type: 'straight' | 'smoothstep' | 'step' | 'bezier' - labelBgColor: string - labelTextColor: string - edgeColor: string - animate: boolean - noHandle: boolean - arrow: boolean - clickCallback: Function - className: string - delete: Function - setExportableData: Function -} - -export interface PotentialAnchorType { - id: string - nodeId: string - callback: Function // callback is used to calculate positionX, positionY based on parent node's data, and set the anchor position // TODO: rename to something better - positionX: number - positionY: number - angle: number - canvasId: string - delete: Function -} - -export interface TemporaryEdgeType { - id: string - sourcePotentialAnchorId: string // this will always be set - sourceX: number - sourceY: number - targetPotentialAnchorId: string | null // this will be null until the temporary edge reaches another temporary anchor - targetX: number - targetY: number - canvasId: string - type: string - edgeColor: string - createEdge: Function - createNode: Function -} diff --git a/frontend/src/lib/components/graph/svelvet/types/README.md b/frontend/src/lib/components/graph/svelvet/types/README.md deleted file mode 100644 index 85773d6efc..0000000000 --- a/frontend/src/lib/components/graph/svelvet/types/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Types - -This folder contains types exported to the user, as decribed in feature: `https://www.svelvet.io/docs/typescript/` diff --git a/frontend/src/lib/components/graph/svelvet/types/index.ts b/frontend/src/lib/components/graph/svelvet/types/index.ts deleted file mode 100644 index 22937de732..0000000000 --- a/frontend/src/lib/components/graph/svelvet/types/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -// this file exports user types -import type { UserEdgeType, UserNodeType } from './types' -export type { UserEdgeType, UserNodeType } diff --git a/frontend/src/lib/components/graph/svelvet/types/types.ts b/frontend/src/lib/components/graph/svelvet/types/types.ts deleted file mode 100644 index b24b3672ed..0000000000 --- a/frontend/src/lib/components/graph/svelvet/types/types.ts +++ /dev/null @@ -1,60 +0,0 @@ -export interface UserNodeType { - id: string - width: number - height: number - bgColor?: string - data: { - html?: any - custom?: { component: any; props?: any; cb?: (e: string, detail: any) => void } - img?: any - } - position: { x: number; y: number } - borderColor?: string | undefined - image?: boolean - src?: string - textColor?: string - targetPosition?: 'left' | 'right' | 'top' | 'bottom' - sourcePosition?: 'left' | 'right' | 'top' | 'bottom' - borderRadius?: number - childNodes?: string[] - className?: string - clickCallback?: Function -} - -export interface UserEdgeType { - id: string - source: string - target: string - sourceAnchorCb?: Function - targetAnchorCb?: Function - label?: string - labelBgColor?: string - labelTextColor?: string - edgeColor?: string - type?: 'straight' | 'smoothstep' | 'step' | 'bezier' | undefined - animate?: boolean - noHandle?: boolean - arrow?: boolean - clickCallback?: Function - className?: string - offset?: number -} - -import { findStore } from '../store/models/store' -import { get } from 'svelte/store' -export function getD3PositionX(canvasId: string) { - const store = findStore(canvasId) - const width = get(store.widthStore) - const x = width / 2 - get(store.d3ZoomParameters).x // user input is shifted so that x=0, y=0 occurs in the center - return x -} -export function getD3PositionY(canvasId: string) { - const store = findStore(canvasId) - const height = get(store.heightStore) - const y = height / 2 - get(store.d3ZoomParameters).y // user input is shifted so that x=0, y=0 occurs in the center - return y -} -export function getD3Zoom(canvasId: string) { - const store = findStore(canvasId) - return get(store.d3ZoomParameters).k -} diff --git a/frontend/src/lib/components/graph/util.ts b/frontend/src/lib/components/graph/util.ts index 0c870dad16..f3c3d1bbb3 100644 --- a/frontend/src/lib/components/graph/util.ts +++ b/frontend/src/lib/components/graph/util.ts @@ -9,16 +9,10 @@ export const NODE = { } } -export function* createIdGenerator(): Generator { - let id = 0 - while (true) { - yield id++ - } -} - export function getStateColor( state: FlowStatusModule['type'] | undefined, - isDark: boolean + isDark: boolean, + lightModeBackground: string = '#dfe6ee' ): string { switch (state) { case 'Success': @@ -32,6 +26,6 @@ export function getStateColor( case 'WaitingForExecutor': return isDark ? '#ea580c' : 'rgb(255, 208, 193)' default: - return isDark ? '#2e3440' : '#fff' + return isDark ? '#2e3440' : lightModeBackground } } diff --git a/frontend/src/routes/approve/[workspace]/[job]/[resume]/[hmac]/+page.svelte b/frontend/src/routes/approve/[workspace]/[job]/[resume]/[hmac]/+page.svelte index a4e7e681d1..645bc58c20 100644 --- a/frontend/src/routes/approve/[workspace]/[job]/[resume]/[hmac]/+page.svelte +++ b/frontend/src/routes/approve/[workspace]/[job]/[resume]/[hmac]/+page.svelte @@ -9,7 +9,6 @@ import JobArgs from '$lib/components/JobArgs.svelte' import { onDestroy, onMount } from 'svelte' import Tooltip from '$lib/components/Tooltip.svelte' - import FlowGraph from '$lib/components/graph/FlowGraph.svelte' import SchemaForm from '$lib/components/SchemaForm.svelte' import { enterpriseLicense, userStore, workspaceStore } from '$lib/stores' import { LogIn, AlertTriangle } from 'lucide-svelte' @@ -20,6 +19,7 @@ import { setLicense } from '$lib/enterpriseUtils' import DisplayResult from '$lib/components/DisplayResult.svelte' import ScheduleEditor from '$lib/components/ScheduleEditor.svelte' + import FlowGraphV2 from '$lib/components/graph/FlowGraphV2.svelte' $workspaceStore = $page.params.workspace let rd = $page.url.href.replace($page.url.origin, '') @@ -286,7 +286,7 @@ {#if job && job.raw_flow && !completed}

Flow details

- - import FlowGraph from '$lib/components/graph/FlowGraph.svelte' + import FlowGraphV2 from '$lib/components/graph/FlowGraphV2.svelte' import { decodeState } from '$lib/utils' let content = localStorage.getItem('svelvet') @@ -8,7 +8,7 @@ : { modules: [], failureModule: undefined } - +