Compare commits
5 Commits
rf/pkce
...
rf/exportI
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
46ddbcc254 | ||
|
|
fcab562bf2 | ||
|
|
56616d5c6d | ||
|
|
471ec6b334 | ||
|
|
56dd32b2f0 |
18
README.md
18
README.md
@@ -30,7 +30,7 @@ Open-source developer infrastructure for internal tools. Self-hostable alternati
|
||||
|
||||
**Hub**: <https://hub.windmill.dev>
|
||||
|
||||
**Contributor's guide**: <https://docs.windmill.dev/docs/misc/contributing>
|
||||
**Contributor's guide**: <https://docs.windmill.dev/docs/contributors_guide>
|
||||
|
||||
**Roadmap**: <https://github.com/orgs/windmill-labs/projects/2>
|
||||
|
||||
@@ -175,7 +175,7 @@ back to the database is ~50ms. A typical lightweight deno job will take around
|
||||
|
||||
We only provide docker-compose setup here. For more advanced setups, like
|
||||
compiling from source or using without a postgres super user, see
|
||||
[documentation](https://docs.windmill.dev/docs/advanced/self_host)
|
||||
[documentation](https://docs.windmill.dev/docs/how-tos/self_host)
|
||||
|
||||
### Docker compose
|
||||
|
||||
@@ -195,8 +195,7 @@ From there, you can create other users (do not forget to change the password!)
|
||||
|
||||
### Kubernetes (k8s) and Helm charts
|
||||
|
||||
We publish helm charts at:
|
||||
<https://github.com/windmill-labs/windmill-helm-charts>
|
||||
We publish helm charts at: <https://github.com/windmill-labs/windmill-helm-charts>
|
||||
|
||||
### Commercial license
|
||||
|
||||
@@ -232,13 +231,12 @@ the following format:
|
||||
|
||||
and mount it at `/usr/src/app/oauth.json`.
|
||||
|
||||
The redirect url for the oauth clients is:
|
||||
`<instance_url>/user/login_callback/<client>`
|
||||
The redirect url for the oauth clients is: `<instance_url>/user/login_callback/<client>`
|
||||
|
||||
[The list of all possible "connect an app" oauth clients](https://github.com/windmill-labs/windmill/blob/main/backend/oauth_connect.json)
|
||||
|
||||
To add more "connect an app" OAuth clients to the Windmill project, read the
|
||||
[Contributor's guide](https://docs.windmill.dev/docs/misc/contributing). We
|
||||
[Contributor's guide](https://docs.windmill.dev/docs/contributors_guide). We
|
||||
welcome contributions!
|
||||
|
||||
You may also add your own custom OAuth2 IdP and OAuth2 Resource provider:
|
||||
@@ -311,12 +309,14 @@ upcoming CLI tool.
|
||||
| QUEUE_LIMIT_WAIT_RESULT | None | The number of max jobs in the queue before rejecting immediately the request in 'run_wait_result' endpoint. Takes precedence on the query arg. If none is specified, there are no limit. | Worker |
|
||||
| DENO_AUTH_TOKENS | None | Custom DENO_AUTH_TOKENS to pass to worker to allow the use of private modules | Worker |
|
||||
| DENO_FLAGS | None | Override the flags passed to deno (default --allow-all) to tighten permissions. Minimum permissions needed are "--allow-read=args.json --allow-write=result.json" | Worker |
|
||||
| PIP_LOCAL_DEPENDENCIES | None | Specify dependencies that are installed locally and do not need to be solved nor installed again | |
|
||||
| PIP_LOCAL_DEPENDENCIES | None | Specify dependencies that are installed locally and do not need to be solved nor installed again |
|
||||
| ADDITIONAL_PYTHON_PATHS | None | Specify python paths (separated by a :) to be appended to the PYTHONPATH of the python jobs. To be used with PIP_LOCAL_DEPENDENCIES to use python codebases within Windmill | Worker |
|
||||
| INCLUDE_HEADERS | None | Whitelist of headers that are passed to jobs as args (separated by a comma) | Server |
|
||||
| WHITELIST_WORKSPACES | None | Whitelist of workspaces this worker takes job from | Worker |
|
||||
| BLACKLIST_WORKSPACES | None | Blacklist of workspaces this worker takes job from | Worker |
|
||||
| NEW_USER_WEBHOOK | None | Webhook to notify of a new user added, signup/invite. Can hook back to windmill to send emails | Server |
|
||||
|
||||
|
||||
|
||||
|
||||
## Run a local dev setup
|
||||
|
||||
|
||||
@@ -1638,21 +1638,6 @@
|
||||
},
|
||||
"query": "SELECT app.id, app.path, app.summary, app.versions, app.policy,\n app.extra_perms, app_version.value, \n app_version.created_at, app_version.created_by from app, app_version \n WHERE app_version.id = $1 AND app.id = app_version.app_id AND app.workspace_id = $2"
|
||||
},
|
||||
"44ded7852b72f50099ff6508a7d13cab73c92ba1d07f2304a3bd3f890fcf3b19": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Jsonb"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "INSERT INTO group_ (workspace_id, name, summary, extra_perms) VALUES ($1, $2, $3, $4) ON CONFLICT DO NOTHING"
|
||||
},
|
||||
"453501fbd61efd26647baf9b6ef702ce0bc2e920914e9f08fe5f2a5f4ab03f02": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
@@ -3542,22 +3527,6 @@
|
||||
},
|
||||
"query": "SELECT value from resource WHERE path = $1 AND (workspace_id = $2 OR workspace_id = 'starter')"
|
||||
},
|
||||
"95ebdfaf0510b9cad861568cd25d479759d4ea3c3ff4e136aad13a3521525372": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"VarcharArray",
|
||||
"Jsonb"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "INSERT INTO folder\n (workspace_id, name, display_name, owners, extra_perms)\n VALUES ($1, $2, $3, $4, $5) ON CONFLICT DO NOTHING"
|
||||
},
|
||||
"9681c348429f5e647ab55a4a54933faa64018f8539a143641f10d14ddd3a09c4": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
@@ -5157,6 +5126,21 @@
|
||||
},
|
||||
"query": "SELECT count(path) FROM app WHERE path LIKE 'f/' || $1 || '%' AND workspace_id = $2"
|
||||
},
|
||||
"d444e1c1e12a82e9aee5c2ffc4d1d3841bd41dd71344ab155c9842b45bcf30b6": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"VarcharArray",
|
||||
"Jsonb"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "INSERT INTO folder\n (workspace_id, name, owners, extra_perms)\n VALUES ($1, $2, $3, $4) ON CONFLICT DO NOTHING"
|
||||
},
|
||||
"d47bff7d6b54cd6da8bb330f7321c37af5dcbd76f9acad73b5ba1b8a4afb5091": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
|
||||
@@ -32,10 +32,9 @@ use sqlx::{Postgres, Transaction};
|
||||
use tokio::{fs::File, io::AsyncReadExt};
|
||||
use tower_cookies::{Cookie, Cookies};
|
||||
use windmill_audit::{audit_log, ActionKind};
|
||||
use windmill_common::users::username_to_permissioned_as;
|
||||
use windmill_common::utils::{not_found_if_none, now_from_db};
|
||||
|
||||
use crate::users::{truncate_token, Authed, NEW_USER_WEBHOOK};
|
||||
use crate::users::{truncate_token, Authed};
|
||||
use crate::workspaces::invite_user_to_all_auto_invite_worspaces;
|
||||
use crate::{
|
||||
db::{UserDB, DB},
|
||||
@@ -96,7 +95,6 @@ pub struct OAuthConfig {
|
||||
extra_params: Option<HashMap<String, String>>,
|
||||
extra_params_callback: Option<HashMap<String, String>>,
|
||||
req_body_auth: Option<bool>,
|
||||
pkce: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
@@ -216,7 +214,6 @@ pub async fn build_oauth_clients(base_url: &str) -> anyhow::Result<AllClients> {
|
||||
extra_params: None,
|
||||
extra_params_callback: None,
|
||||
req_body_auth: None,
|
||||
pkce: None,
|
||||
},
|
||||
v.clone(),
|
||||
false,
|
||||
@@ -660,26 +657,14 @@ async fn connect_slack_callback(
|
||||
)
|
||||
.execute(&mut tx)
|
||||
.await?;
|
||||
sqlx::query_as!(
|
||||
Group,
|
||||
"INSERT INTO group_ (workspace_id, name, summary, extra_perms) VALUES ($1, $2, $3, $4) ON CONFLICT DO NOTHING",
|
||||
w_id,
|
||||
"slack",
|
||||
"The group slack commands act on belhalf of",
|
||||
serde_json::json!({username_to_permissioned_as(&authed.username): true})
|
||||
)
|
||||
.execute(&mut tx)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
"INSERT INTO folder
|
||||
(workspace_id, name, display_name, owners, extra_perms)
|
||||
VALUES ($1, $2, $3, $4, $5) ON CONFLICT DO NOTHING",
|
||||
(workspace_id, name, owners, extra_perms)
|
||||
VALUES ($1, $2, $3, $4) ON CONFLICT DO NOTHING",
|
||||
&w_id,
|
||||
"slack_bot",
|
||||
"Slack bot",
|
||||
&["g/slack".to_string()],
|
||||
serde_json::json!({"g/slack": true})
|
||||
&[],
|
||||
serde_json::json!({})
|
||||
)
|
||||
.execute(&mut tx)
|
||||
.await?;
|
||||
@@ -976,7 +961,6 @@ async fn login_callback(
|
||||
Some([("method", &client_name[..])].into()),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let demo_exists =
|
||||
sqlx::query_scalar!("SELECT EXISTS(SELECT 1 FROM workspace WHERE id = 'demo')")
|
||||
.fetch_one(&mut tx)
|
||||
@@ -998,16 +982,6 @@ async fn login_callback(
|
||||
}
|
||||
}
|
||||
tx.commit().await?;
|
||||
|
||||
if let Some(new_user_webhook) = NEW_USER_WEBHOOK.clone() {
|
||||
let _ = http_client
|
||||
.post(&new_user_webhook)
|
||||
.json(&serde_json::json!({"email" : &email, "event": "oauth_signup"}))
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| tracing::error!("Error sending new user webhook: {}", e.to_string()));
|
||||
}
|
||||
|
||||
Ok("Successfully logged in".to_string())
|
||||
} else {
|
||||
Err(error::Error::BadRequest(format!(
|
||||
@@ -1113,19 +1087,14 @@ fn oauth_redirect(
|
||||
}
|
||||
|
||||
let mut auth_url = client.authorize_url(&state);
|
||||
{
|
||||
let mut query_string = auth_url.query_pairs_mut();
|
||||
|
||||
let pkce = PkceCodeVerifierS256::new_random();
|
||||
for (key, value) in pkce.authorize_url_params() {
|
||||
if let Some(extra_params) = extra_params {
|
||||
let mut query_string = auth_url.query_pairs_mut();
|
||||
for (key, value) in extra_params {
|
||||
query_string.append_pair(&key, &value);
|
||||
}
|
||||
if let Some(extra_params) = extra_params {
|
||||
for (key, value) in extra_params {
|
||||
query_string.append_pair(&key, &value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set_cookie(&state, cookies, is_secure);
|
||||
Ok(Redirect::to(auth_url.as_str()))
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ use axum::{
|
||||
};
|
||||
use hyper::{header::LOCATION, StatusCode};
|
||||
use rand::rngs::OsRng;
|
||||
use reqwest::Client;
|
||||
use retainer::Cache;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::FromRow;
|
||||
@@ -1271,15 +1270,10 @@ async fn delete_user(
|
||||
Ok(format!("email {} deleted", &email_to_delete))
|
||||
}
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref NEW_USER_WEBHOOK: Option<String> = std::env::var("NEW_USER_WEBHOOK").ok();
|
||||
}
|
||||
|
||||
async fn create_user(
|
||||
Authed { email, .. }: Authed,
|
||||
Extension(db): Extension<DB>,
|
||||
Extension(argon2): Extension<Arc<Argon2<'_>>>,
|
||||
Extension(http_client): Extension<Client>,
|
||||
Json(nu): Json<NewUser>,
|
||||
) -> Result<(StatusCode, String)> {
|
||||
let mut tx = db.begin().await?;
|
||||
@@ -1311,16 +1305,6 @@ async fn create_user(
|
||||
)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
|
||||
if let Some(new_user_webhook) = NEW_USER_WEBHOOK.clone() {
|
||||
let _ = http_client
|
||||
.post(&new_user_webhook)
|
||||
.json(&serde_json::json!({"email" : &nu.email, "name": &nu.name, "event": "new_user"}))
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| tracing::error!("Error sending new user webhook: {}", e.to_string()));
|
||||
}
|
||||
|
||||
invite_user_to_all_auto_invite_worspaces(&db, &nu.email).await?;
|
||||
|
||||
Ok((StatusCode::CREATED, format!("email {} created", nu.email)))
|
||||
|
||||
@@ -12,7 +12,7 @@ use crate::{
|
||||
db::{UserDB, DB},
|
||||
folders::Folder,
|
||||
resources::{Resource, ResourceType},
|
||||
users::{Authed, WorkspaceInvite, NEW_USER_WEBHOOK},
|
||||
users::{Authed, WorkspaceInvite},
|
||||
utils::require_super_admin,
|
||||
BaseUrl,
|
||||
};
|
||||
@@ -24,7 +24,6 @@ use axum::{
|
||||
routing::{delete, get, post},
|
||||
Json, Router,
|
||||
};
|
||||
use reqwest::Client;
|
||||
use stripe::CustomerId;
|
||||
use windmill_audit::{audit_log, ActionKind};
|
||||
use windmill_common::{
|
||||
@@ -926,7 +925,6 @@ pub async fn invite_user_to_all_auto_invite_worspaces(db: &DB, email: &str) -> R
|
||||
async fn invite_user(
|
||||
Authed { username, is_admin, .. }: Authed,
|
||||
Extension(db): Extension<DB>,
|
||||
Extension(http_client): Extension<Client>,
|
||||
Path(w_id): Path<String>,
|
||||
Json(nu): Json<NewWorkspaceInvite>,
|
||||
) -> Result<(StatusCode, String)> {
|
||||
@@ -948,15 +946,6 @@ async fn invite_user(
|
||||
|
||||
tx.commit().await?;
|
||||
|
||||
if let Some(new_user_webhook) = NEW_USER_WEBHOOK.clone() {
|
||||
let _ = http_client
|
||||
.post(&new_user_webhook)
|
||||
.json(&serde_json::json!({"email" : &nu.email, "event": "new_invite"}))
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| tracing::error!("Error sending new user webhook: {}", e.to_string()));
|
||||
}
|
||||
|
||||
Ok((
|
||||
StatusCode::CREATED,
|
||||
format!("user with email {} invited", nu.email),
|
||||
|
||||
@@ -1774,7 +1774,7 @@ async fn handle_python_job(
|
||||
} else {
|
||||
sig.args
|
||||
.into_iter()
|
||||
.map(|x| format!("args[\"{}\"] = kwargs.get(\"{}\")", x.name, x.name))
|
||||
.map(|x| format!("args[\"{}\"] = kwargs[\"{}\"]", x.name, x.name))
|
||||
.join("\n")
|
||||
};
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ services:
|
||||
environment:
|
||||
- DATABASE_URL=postgres://postgres:${DB_PASSWORD}@db/windmill?sslmode=disable
|
||||
- BASE_URL=http://${WM_BASE_URL}
|
||||
- BASE_INTERNAL_URL=http://windmill_server:8000
|
||||
- BASE_INTERNAL_URL=http://localhost:8000
|
||||
- RUST_LOG=info
|
||||
- NUM_WORKERS=1
|
||||
- DISABLE_SERVER=true
|
||||
|
||||
1
frontend/.gitignore
vendored
1
frontend/.gitignore
vendored
@@ -1,6 +1,7 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/build
|
||||
/dist
|
||||
/.svelte-kit
|
||||
/package
|
||||
/src/lib/gen
|
||||
|
||||
5
frontend/embed/README.md
Normal file
5
frontend/embed/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Build the app preview embed code
|
||||
|
||||
./embed/build.sh
|
||||
|
||||
serve dist
|
||||
6
frontend/embed/build.sh
Executable file
6
frontend/embed/build.sh
Executable file
@@ -0,0 +1,6 @@
|
||||
mkdir -p dist
|
||||
npm run build-embed
|
||||
cp static/Inter-Variable.ttf dist
|
||||
cp static/tailwind.js dist
|
||||
npx tailwindcss -o src/lib/assets/app.css --minify -o dist/tailwind.css
|
||||
cp embed/index.html dist
|
||||
840
frontend/embed/index.html
Normal file
840
frontend/embed/index.html
Normal file
@@ -0,0 +1,840 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Embed demo</title>
|
||||
<link href="/style.css" rel="stylesheet">
|
||||
<link href="/tailwind.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script src="/appviewer.iife.js">
|
||||
</script>
|
||||
</script>
|
||||
<script>
|
||||
loadApp(document.getElementById("app"), {
|
||||
"grid": [
|
||||
{
|
||||
"3": {
|
||||
"h": 2,
|
||||
"w": 1,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"max": {
|
||||
"h": 100,
|
||||
"w": 3
|
||||
},
|
||||
"min": {
|
||||
"h": 1,
|
||||
"w": 1
|
||||
},
|
||||
"fixed": false,
|
||||
"draggable": true,
|
||||
"resizable": true,
|
||||
"customDragger": false,
|
||||
"customResizer": false
|
||||
},
|
||||
"12": {
|
||||
"h": 5,
|
||||
"w": 3,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"id": "a",
|
||||
"max": {
|
||||
"h": 100,
|
||||
"w": 12
|
||||
},
|
||||
"min": {
|
||||
"h": 1,
|
||||
"w": 1
|
||||
},
|
||||
"fixed": false,
|
||||
"draggable": true,
|
||||
"resizable": true,
|
||||
"customDragger": false,
|
||||
"customResizer": false
|
||||
},
|
||||
"id": "a",
|
||||
"data": {
|
||||
"id": "a",
|
||||
"card": false,
|
||||
"type": "htmlcomponent",
|
||||
"softWrap": false,
|
||||
"configuration": {},
|
||||
"componentInput": {
|
||||
"eval": "<img\n\tsrc=\"https://images.unsplash.com/photo-1554629947-334ff61d85dc?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1024&h=1280&q=80\"\n>\n<div class=\"absolute top-4 left-2 text-white\">\n\tHello ${ctx.username}\n</div>\t\t\t\n",
|
||||
"type": "template",
|
||||
"value": "<img\n\tsrc=\"https://images.unsplash.com/photo-1554629947-334ff61d85dc?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1024&h=1280&q=80\"\n>\n<div class=\"absolute top-4 left-2 text-white\">\n\tHello ${ctx.username}\n</div>\t\t\t\n",
|
||||
"fieldType": "template"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"3": {
|
||||
"h": 10,
|
||||
"w": 3,
|
||||
"x": 0,
|
||||
"y": 2,
|
||||
"max": {
|
||||
"h": 100,
|
||||
"w": 3
|
||||
},
|
||||
"min": {
|
||||
"h": 1,
|
||||
"w": 1
|
||||
},
|
||||
"fixed": false,
|
||||
"draggable": true,
|
||||
"resizable": true,
|
||||
"customDragger": false,
|
||||
"customResizer": false
|
||||
},
|
||||
"12": {
|
||||
"h": 10,
|
||||
"w": 6,
|
||||
"x": 3,
|
||||
"y": 0,
|
||||
"id": "b",
|
||||
"max": {
|
||||
"h": 100,
|
||||
"w": 12
|
||||
},
|
||||
"min": {
|
||||
"h": 1,
|
||||
"w": 1
|
||||
},
|
||||
"fixed": false,
|
||||
"draggable": true,
|
||||
"resizable": true,
|
||||
"customDragger": false,
|
||||
"customResizer": false
|
||||
},
|
||||
"id": "b",
|
||||
"data": {
|
||||
"id": "b",
|
||||
"card": true,
|
||||
"type": "tablecomponent",
|
||||
"actionButtons": [],
|
||||
"configuration": {
|
||||
"search": {
|
||||
"type": "static",
|
||||
"value": "Disabled",
|
||||
"fieldType": "select",
|
||||
"onlyStatic": true,
|
||||
"optionValuesKey": "tableSearchOptions"
|
||||
}
|
||||
},
|
||||
"componentInput": {
|
||||
"type": "static",
|
||||
"value": [
|
||||
{
|
||||
"id": 1,
|
||||
"age": 42,
|
||||
"name": "A cell with a long name"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"age": 84,
|
||||
"name": "A briefer cell"
|
||||
}
|
||||
],
|
||||
"fieldType": "array",
|
||||
"subFieldType": "object"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"3": {
|
||||
"h": 8,
|
||||
"w": 2,
|
||||
"x": 0,
|
||||
"y": 12,
|
||||
"max": {
|
||||
"h": 100,
|
||||
"w": 3
|
||||
},
|
||||
"min": {
|
||||
"h": 1,
|
||||
"w": 1
|
||||
},
|
||||
"fixed": false,
|
||||
"draggable": true,
|
||||
"resizable": true,
|
||||
"customDragger": false,
|
||||
"customResizer": false
|
||||
},
|
||||
"12": {
|
||||
"h": 8,
|
||||
"w": 6,
|
||||
"x": 0,
|
||||
"y": 10,
|
||||
"max": {
|
||||
"h": 100,
|
||||
"w": 12
|
||||
},
|
||||
"min": {
|
||||
"h": 1,
|
||||
"w": 1
|
||||
},
|
||||
"fixed": false,
|
||||
"draggable": true,
|
||||
"resizable": true,
|
||||
"customDragger": false,
|
||||
"customResizer": false
|
||||
},
|
||||
"id": "c",
|
||||
"data": {
|
||||
"id": "c",
|
||||
"card": true,
|
||||
"type": "scatterchartcomponent",
|
||||
"configuration": {
|
||||
"pannable": {
|
||||
"type": "static",
|
||||
"value": false,
|
||||
"fieldType": "boolean",
|
||||
"onlyStatic": true
|
||||
},
|
||||
"zoomable": {
|
||||
"type": "static",
|
||||
"value": false,
|
||||
"fieldType": "boolean",
|
||||
"onlyStatic": true
|
||||
}
|
||||
},
|
||||
"componentInput": {
|
||||
"type": "static",
|
||||
"value": [
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"x": 25,
|
||||
"y": 50
|
||||
},
|
||||
{
|
||||
"x": 23,
|
||||
"y": 23
|
||||
},
|
||||
{
|
||||
"x": 12,
|
||||
"y": 37
|
||||
}
|
||||
],
|
||||
"label": "foo",
|
||||
"backgroundColor": "rgb(255, 12, 137)"
|
||||
},
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
{
|
||||
"x": 25,
|
||||
"y": 42
|
||||
},
|
||||
{
|
||||
"x": 3,
|
||||
"y": 27
|
||||
}
|
||||
],
|
||||
"label": "foobar",
|
||||
"backgroundColor": "orange"
|
||||
}
|
||||
],
|
||||
"fieldType": "array",
|
||||
"subFieldType": "object"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"3": {
|
||||
"h": 8,
|
||||
"w": 2,
|
||||
"x": 0,
|
||||
"y": 20,
|
||||
"max": {
|
||||
"h": 100,
|
||||
"w": 3
|
||||
},
|
||||
"min": {
|
||||
"h": 1,
|
||||
"w": 1
|
||||
},
|
||||
"fixed": false,
|
||||
"draggable": true,
|
||||
"resizable": true,
|
||||
"customDragger": false,
|
||||
"customResizer": false
|
||||
},
|
||||
"12": {
|
||||
"h": 8,
|
||||
"w": 6,
|
||||
"x": 6,
|
||||
"y": 10,
|
||||
"id": "d",
|
||||
"max": {
|
||||
"h": 100,
|
||||
"w": 12
|
||||
},
|
||||
"min": {
|
||||
"h": 1,
|
||||
"w": 1
|
||||
},
|
||||
"fixed": false,
|
||||
"draggable": true,
|
||||
"resizable": true,
|
||||
"customDragger": false,
|
||||
"customResizer": false
|
||||
},
|
||||
"id": "d",
|
||||
"data": {
|
||||
"id": "d",
|
||||
"card": false,
|
||||
"type": "displaycomponent",
|
||||
"configuration": {},
|
||||
"componentInput": {
|
||||
"type": "connected",
|
||||
"value": {
|
||||
"foo": 42
|
||||
},
|
||||
"fields": {
|
||||
"x": {
|
||||
"type": "connected",
|
||||
"value": "gooo",
|
||||
"visible": true,
|
||||
"fieldType": "string",
|
||||
"connection": {
|
||||
"path": "result",
|
||||
"componentId": "e"
|
||||
}
|
||||
}
|
||||
},
|
||||
"runnable": {
|
||||
"name": "Inline Script 1",
|
||||
"type": "runnableByName",
|
||||
"inlineScript": {
|
||||
"path": "/inline-script/Inline Script 1",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"required": [
|
||||
"x"
|
||||
],
|
||||
"properties": {
|
||||
"x": {
|
||||
"type": "string",
|
||||
"default": null,
|
||||
"description": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"content": "// import * as wmill from \"https://deno.land/x/windmill@v1.57.1/mod.ts\"\n\nexport async function main(x: string) {\n return x\n}\n",
|
||||
"language": "deno"
|
||||
}
|
||||
},
|
||||
"fieldType": "object",
|
||||
"connection": {
|
||||
"path": "result",
|
||||
"componentId": "e"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"3": {
|
||||
"h": 1,
|
||||
"w": 2,
|
||||
"x": 1,
|
||||
"y": 0,
|
||||
"max": {
|
||||
"h": 100,
|
||||
"w": 3
|
||||
},
|
||||
"min": {
|
||||
"h": 1,
|
||||
"w": 1
|
||||
},
|
||||
"fixed": false,
|
||||
"draggable": true,
|
||||
"resizable": true,
|
||||
"customDragger": false,
|
||||
"customResizer": false
|
||||
},
|
||||
"12": {
|
||||
"h": 1,
|
||||
"w": 3,
|
||||
"x": 9,
|
||||
"y": 0,
|
||||
"id": "e",
|
||||
"max": {
|
||||
"h": 100,
|
||||
"w": 12
|
||||
},
|
||||
"min": {
|
||||
"h": 1,
|
||||
"w": 1
|
||||
},
|
||||
"fixed": false,
|
||||
"draggable": true,
|
||||
"resizable": true,
|
||||
"customDragger": false,
|
||||
"customResizer": false
|
||||
},
|
||||
"id": "e",
|
||||
"data": {
|
||||
"id": "e",
|
||||
"card": false,
|
||||
"type": "textinputcomponent",
|
||||
"softWrap": true,
|
||||
"configuration": {
|
||||
"placeholder": {
|
||||
"type": "static",
|
||||
"value": "Type...",
|
||||
"fieldType": "text",
|
||||
"onlyStatic": true
|
||||
}
|
||||
},
|
||||
"verticalAlignment": "center"
|
||||
}
|
||||
},
|
||||
{
|
||||
"3": {
|
||||
"h": 1,
|
||||
"w": 2,
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"max": {
|
||||
"h": 100,
|
||||
"w": 3
|
||||
},
|
||||
"min": {
|
||||
"h": 1,
|
||||
"w": 1
|
||||
},
|
||||
"fixed": false,
|
||||
"draggable": true,
|
||||
"resizable": true,
|
||||
"customDragger": false,
|
||||
"customResizer": false
|
||||
},
|
||||
"12": {
|
||||
"h": 1,
|
||||
"w": 3,
|
||||
"x": 9,
|
||||
"y": 1,
|
||||
"max": {
|
||||
"h": 100,
|
||||
"w": 12
|
||||
},
|
||||
"min": {
|
||||
"h": 1,
|
||||
"w": 1
|
||||
},
|
||||
"fixed": false,
|
||||
"draggable": true,
|
||||
"resizable": true,
|
||||
"customDragger": false,
|
||||
"customResizer": false
|
||||
},
|
||||
"id": "f",
|
||||
"data": {
|
||||
"id": "f",
|
||||
"card": false,
|
||||
"type": "passwordinputcomponent",
|
||||
"softWrap": true,
|
||||
"configuration": {
|
||||
"placeholder": {
|
||||
"type": "static",
|
||||
"value": "Password",
|
||||
"fieldType": "text",
|
||||
"onlyStatic": true
|
||||
}
|
||||
},
|
||||
"verticalAlignment": "center"
|
||||
}
|
||||
},
|
||||
{
|
||||
"3": {
|
||||
"h": 1,
|
||||
"w": 2,
|
||||
"x": 0,
|
||||
"y": 28,
|
||||
"max": {
|
||||
"h": 100,
|
||||
"w": 3
|
||||
},
|
||||
"min": {
|
||||
"h": 1,
|
||||
"w": 1
|
||||
},
|
||||
"fixed": false,
|
||||
"draggable": true,
|
||||
"resizable": true,
|
||||
"customDragger": false,
|
||||
"customResizer": false
|
||||
},
|
||||
"12": {
|
||||
"h": 1,
|
||||
"w": 3,
|
||||
"x": 9,
|
||||
"y": 2,
|
||||
"max": {
|
||||
"h": 100,
|
||||
"w": 12
|
||||
},
|
||||
"min": {
|
||||
"h": 1,
|
||||
"w": 1
|
||||
},
|
||||
"fixed": false,
|
||||
"draggable": true,
|
||||
"resizable": true,
|
||||
"customDragger": false,
|
||||
"customResizer": false
|
||||
},
|
||||
"id": "g",
|
||||
"data": {
|
||||
"id": "g",
|
||||
"card": false,
|
||||
"type": "dateinputcomponent",
|
||||
"softWrap": true,
|
||||
"configuration": {
|
||||
"maxDate": {
|
||||
"type": "static",
|
||||
"value": "",
|
||||
"fieldType": "date"
|
||||
},
|
||||
"minDate": {
|
||||
"type": "static",
|
||||
"value": "",
|
||||
"fieldType": "date"
|
||||
}
|
||||
},
|
||||
"verticalAlignment": "center"
|
||||
}
|
||||
},
|
||||
{
|
||||
"3": {
|
||||
"h": 1,
|
||||
"w": 1,
|
||||
"x": 2,
|
||||
"y": 12,
|
||||
"max": {
|
||||
"h": 100,
|
||||
"w": 3
|
||||
},
|
||||
"min": {
|
||||
"h": 1,
|
||||
"w": 1
|
||||
},
|
||||
"fixed": false,
|
||||
"draggable": true,
|
||||
"resizable": true,
|
||||
"customDragger": false,
|
||||
"customResizer": false
|
||||
},
|
||||
"12": {
|
||||
"h": 1,
|
||||
"w": 2,
|
||||
"x": 9,
|
||||
"y": 3,
|
||||
"max": {
|
||||
"h": 100,
|
||||
"w": 12
|
||||
},
|
||||
"min": {
|
||||
"h": 1,
|
||||
"w": 1
|
||||
},
|
||||
"fixed": false,
|
||||
"draggable": true,
|
||||
"resizable": true,
|
||||
"customDragger": false,
|
||||
"customResizer": false
|
||||
},
|
||||
"id": "h",
|
||||
"data": {
|
||||
"id": "h",
|
||||
"card": false,
|
||||
"type": "checkboxcomponent",
|
||||
"softWrap": true,
|
||||
"configuration": {
|
||||
"label": {
|
||||
"type": "static",
|
||||
"value": "Label",
|
||||
"fieldType": "text"
|
||||
}
|
||||
},
|
||||
"verticalAlignment": "center",
|
||||
"horizontalAlignment": "center"
|
||||
}
|
||||
},
|
||||
{
|
||||
"3": {
|
||||
"h": 1,
|
||||
"w": 2,
|
||||
"x": 0,
|
||||
"y": 29,
|
||||
"max": {
|
||||
"h": 100,
|
||||
"w": 3
|
||||
},
|
||||
"min": {
|
||||
"h": 1,
|
||||
"w": 1
|
||||
},
|
||||
"fixed": false,
|
||||
"draggable": true,
|
||||
"resizable": true,
|
||||
"customDragger": false,
|
||||
"customResizer": false
|
||||
},
|
||||
"12": {
|
||||
"h": 1,
|
||||
"w": 3,
|
||||
"x": 9,
|
||||
"y": 4,
|
||||
"max": {
|
||||
"h": 100,
|
||||
"w": 12
|
||||
},
|
||||
"min": {
|
||||
"h": 1,
|
||||
"w": 1
|
||||
},
|
||||
"fixed": false,
|
||||
"draggable": true,
|
||||
"resizable": true,
|
||||
"customDragger": false,
|
||||
"customResizer": false
|
||||
},
|
||||
"id": "i",
|
||||
"data": {
|
||||
"id": "i",
|
||||
"card": false,
|
||||
"type": "selectcomponent",
|
||||
"softWrap": true,
|
||||
"configuration": {
|
||||
"items": {
|
||||
"type": "static",
|
||||
"value": [
|
||||
{
|
||||
"label": "Foo",
|
||||
"value": "foo"
|
||||
},
|
||||
{
|
||||
"label": "Bar",
|
||||
"value": "bar"
|
||||
}
|
||||
],
|
||||
"fieldType": "array",
|
||||
"subFieldType": "object"
|
||||
},
|
||||
"itemKey": {
|
||||
"type": "static",
|
||||
"value": "value",
|
||||
"fieldType": "text"
|
||||
}
|
||||
},
|
||||
"verticalAlignment": "center"
|
||||
}
|
||||
},
|
||||
{
|
||||
"3": {
|
||||
"h": 1,
|
||||
"w": 1,
|
||||
"x": 2,
|
||||
"y": 13,
|
||||
"max": {
|
||||
"h": 100,
|
||||
"w": 3
|
||||
},
|
||||
"min": {
|
||||
"h": 1,
|
||||
"w": 1
|
||||
},
|
||||
"fixed": false,
|
||||
"draggable": true,
|
||||
"resizable": true,
|
||||
"customDragger": false,
|
||||
"customResizer": false
|
||||
},
|
||||
"12": {
|
||||
"h": 1,
|
||||
"w": 2,
|
||||
"x": 0,
|
||||
"y": 5,
|
||||
"max": {
|
||||
"h": 100,
|
||||
"w": 12
|
||||
},
|
||||
"min": {
|
||||
"h": 1,
|
||||
"w": 1
|
||||
},
|
||||
"fixed": false,
|
||||
"draggable": true,
|
||||
"resizable": true,
|
||||
"customDragger": false,
|
||||
"customResizer": false
|
||||
},
|
||||
"id": "j",
|
||||
"data": {
|
||||
"id": "j",
|
||||
"card": false,
|
||||
"type": "buttoncomponent",
|
||||
"softWrap": true,
|
||||
"configuration": {
|
||||
"size": {
|
||||
"type": "static",
|
||||
"value": "xs",
|
||||
"fieldType": "select",
|
||||
"onlyStatic": true,
|
||||
"optionValuesKey": "buttonSizeOptions"
|
||||
},
|
||||
"color": {
|
||||
"type": "static",
|
||||
"value": "blue",
|
||||
"fieldType": "select",
|
||||
"onlyStatic": true,
|
||||
"optionValuesKey": "buttonColorOptions"
|
||||
},
|
||||
"label": {
|
||||
"type": "static",
|
||||
"value": "Press me",
|
||||
"fieldType": "text"
|
||||
}
|
||||
},
|
||||
"componentInput": {
|
||||
"type": "runnable",
|
||||
"fields": {},
|
||||
"fieldType": "any"
|
||||
},
|
||||
"verticalAlignment": "center",
|
||||
"horizontalAlignment": "center"
|
||||
}
|
||||
},
|
||||
{
|
||||
"3": {
|
||||
"h": 5,
|
||||
"w": 3,
|
||||
"x": 0,
|
||||
"y": 30,
|
||||
"max": {
|
||||
"h": 100,
|
||||
"w": 3
|
||||
},
|
||||
"min": {
|
||||
"h": 1,
|
||||
"w": 1
|
||||
},
|
||||
"fixed": false,
|
||||
"draggable": true,
|
||||
"resizable": true,
|
||||
"customDragger": false,
|
||||
"customResizer": false
|
||||
},
|
||||
"12": {
|
||||
"h": 5,
|
||||
"w": 6,
|
||||
"x": 0,
|
||||
"y": 18,
|
||||
"id": "k",
|
||||
"max": {
|
||||
"h": 100,
|
||||
"w": 12
|
||||
},
|
||||
"min": {
|
||||
"h": 1,
|
||||
"w": 1
|
||||
},
|
||||
"fixed": false,
|
||||
"draggable": true,
|
||||
"resizable": true,
|
||||
"customDragger": false,
|
||||
"customResizer": false
|
||||
},
|
||||
"id": "k",
|
||||
"data": {
|
||||
"id": "k",
|
||||
"card": true,
|
||||
"type": "formcomponent",
|
||||
"configuration": {
|
||||
"size": {
|
||||
"type": "static",
|
||||
"value": "xs",
|
||||
"fieldType": "select",
|
||||
"onlyStatic": true,
|
||||
"optionValuesKey": "buttonSizeOptions"
|
||||
},
|
||||
"color": {
|
||||
"type": "static",
|
||||
"value": "dark",
|
||||
"fieldType": "select",
|
||||
"onlyStatic": true,
|
||||
"optionValuesKey": "buttonColorOptions"
|
||||
},
|
||||
"label": {
|
||||
"type": "static",
|
||||
"value": "Submit",
|
||||
"fieldType": "text"
|
||||
}
|
||||
},
|
||||
"componentInput": {
|
||||
"type": "runnable",
|
||||
"fields": {
|
||||
"x": {
|
||||
"type": "user",
|
||||
"value": null,
|
||||
"visible": true,
|
||||
"fieldType": "string"
|
||||
},
|
||||
"y": {
|
||||
"type": "user",
|
||||
"value": null,
|
||||
"visible": true,
|
||||
"fieldType": "string"
|
||||
}
|
||||
},
|
||||
"runnable": {
|
||||
"name": "Inline Script 0",
|
||||
"type": "runnableByName",
|
||||
"inlineScript": {
|
||||
"path": "/inline-script/Inline Script 0",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"required": [
|
||||
"x",
|
||||
"y"
|
||||
],
|
||||
"properties": {
|
||||
"x": {
|
||||
"type": "string",
|
||||
"default": null,
|
||||
"description": ""
|
||||
},
|
||||
"y": {
|
||||
"type": "string",
|
||||
"default": null,
|
||||
"description": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"content": "// import * as wmill from \"https://deno.land/x/windmill@v1.57.1/mod.ts\"\n\nexport async function main(x: string, y: string) {\n return x\n}\n",
|
||||
"language": "deno"
|
||||
}
|
||||
},
|
||||
"fieldType": "any"
|
||||
},
|
||||
"horizontalAlignment": "center"
|
||||
}
|
||||
}
|
||||
],
|
||||
"fullscreen": false,
|
||||
"unusedInlineScripts": []
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
710
frontend/package-lock.json
generated
710
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,7 @@
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
"build-embed": "vite build -c vite.embed.config.js",
|
||||
"preview": "vite preview",
|
||||
"check": "svelte-check --tsconfig ./tsconfig.json --threshold warning",
|
||||
"check:watch": "svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
@@ -16,6 +17,11 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.29.2",
|
||||
"@rollup/plugin-commonjs": "^24.0.1",
|
||||
"@rollup/plugin-html": "^1.0.2",
|
||||
"@rollup/plugin-node-resolve": "^15.0.1",
|
||||
"@rollup/plugin-terser": "^0.4.0",
|
||||
"@rollup/plugin-typescript": "^11.0.0",
|
||||
"@sveltejs/adapter-static": "^1.0.0",
|
||||
"@sveltejs/kit": "^1.0.0-next.589",
|
||||
"@sveltejs/package": "^1.0.2",
|
||||
@@ -34,15 +40,17 @@
|
||||
"eslint-config-prettier": "^8.6.0",
|
||||
"eslint-plugin-svelte3": "^4.0.0",
|
||||
"lucide-svelte": "^0.104.0",
|
||||
"monaco-editor-workers": "0.33.0",
|
||||
"openapi-typescript-codegen": "^0.23.0",
|
||||
"path-browserify": "^1.0.1",
|
||||
"postcss": "^8.4.18",
|
||||
"postcss-load-config": "^4.0.1",
|
||||
"prettier": "^2.8.3",
|
||||
"prettier-plugin-svelte": "^2.9.0",
|
||||
"rollup-plugin-svelte": "^7.1.0",
|
||||
"simple-svelte-autocomplete": "^2.5.1",
|
||||
"stylelint-config-recommended": "^9.0.0",
|
||||
"svelte": "^3.53.1",
|
||||
"svelte": "^3.55.1",
|
||||
"svelte-awesome": "^3.0.0",
|
||||
"svelte-check": "^3.0.2",
|
||||
"svelte-dnd-action": "^0.9.22",
|
||||
@@ -67,6 +75,7 @@
|
||||
"@redocly/json-to-json-schema": "^0.0.1",
|
||||
"@tanstack/svelte-table": "^8.7.6",
|
||||
"async-mutex": "^0.4.0",
|
||||
"chart.js": "3.8.2",
|
||||
"chartjs-adapter-date-fns": "^3.0.0",
|
||||
"chartjs-plugin-zoom": "^2.0.0",
|
||||
"date-fns": "^2.29.3",
|
||||
@@ -83,5 +92,6 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"svelte": "^3.53.1"
|
||||
}
|
||||
},
|
||||
"main": "index.js"
|
||||
}
|
||||
|
||||
9
frontend/src/embed.js
Normal file
9
frontend/src/embed.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import AppPreview from './lib/components/apps/editor/AppPreview.svelte'
|
||||
|
||||
globalThis.process = { env: { NODE_ENV: 'production' } }
|
||||
globalThis.loadApp = (target, value) => {
|
||||
new AppPreview({
|
||||
target: target,
|
||||
props: { app: value }
|
||||
})
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { truncate } from '$lib/utils'
|
||||
import Tooltip from './Tooltip.svelte'
|
||||
import json from 'svelte-highlight/languages/json'
|
||||
import { Highlight } from 'svelte-highlight'
|
||||
import { ResourceService } from '$lib/gen'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import Drawer from './common/drawer/Drawer.svelte'
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
| 'error'
|
||||
| 'approval'
|
||||
| 'svg'
|
||||
| 'filename'
|
||||
| undefined
|
||||
|
||||
$: resultKind = inferResultKind(result)
|
||||
@@ -71,8 +70,6 @@
|
||||
return 'file'
|
||||
} else if (keys.length == 1 && keys[0] == 'error') {
|
||||
return 'error'
|
||||
} else if (keys.length === 2 && keys.includes('file') && keys.includes('filename')) {
|
||||
return 'filename'
|
||||
} else if (
|
||||
keys.length == 3 &&
|
||||
keys.includes('resume') &&
|
||||
@@ -186,12 +183,6 @@
|
||||
>Download</a
|
||||
>
|
||||
</div>
|
||||
{:else if !forceJson && resultKind === 'filename'}
|
||||
<div>
|
||||
<a download={result.filename} href="data:application/octet-stream;base64,{result.file}">
|
||||
Download
|
||||
</a>
|
||||
</div>
|
||||
{:else if !forceJson && resultKind == 'error'}<div>
|
||||
<span class="text-red-500 font-semibold text-sm whitespace-pre-wrap"
|
||||
>{result.error.name}: {result.error.message}</span
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'
|
||||
import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker'
|
||||
|
||||
import { buildWorkerDefinition } from 'monaco-editor-workers'
|
||||
import type {
|
||||
Disposable,
|
||||
DocumentUri,
|
||||
@@ -90,13 +89,7 @@
|
||||
const uri = `file:///tmp/monaco/${hash}.${langToExt(lang)}`
|
||||
|
||||
if (browser) {
|
||||
if (dev) {
|
||||
buildWorkerDefinition(
|
||||
'../../../node_modules/monaco-editor-workers/dist/workers',
|
||||
import.meta.url,
|
||||
false
|
||||
)
|
||||
} else {
|
||||
if (!dev) {
|
||||
// @ts-ignore
|
||||
self.MonacoEnvironment = {
|
||||
getWorker: function (_moduleId: any, label: string) {
|
||||
@@ -408,6 +401,14 @@
|
||||
}
|
||||
|
||||
async function loadMonaco() {
|
||||
if (dev) {
|
||||
let { buildWorkerDefinition } = await import('monaco-editor-workers')
|
||||
buildWorkerDefinition(
|
||||
'../../../node_modules/monaco-editor-workers/dist/workers',
|
||||
import.meta.url,
|
||||
false
|
||||
)
|
||||
}
|
||||
const model = meditor.createModel(code, lang, mUri.parse(uri))
|
||||
|
||||
model.updateOptions(updateOptions)
|
||||
|
||||
@@ -164,14 +164,6 @@
|
||||
let selected: 'graph' | 'sequence' = 'graph'
|
||||
|
||||
let payload: string = '"a test payload in json"'
|
||||
|
||||
function isSuccess(arg: any): boolean | undefined {
|
||||
if (arg == undefined) {
|
||||
return undefined
|
||||
} else {
|
||||
return arg == true
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if job}
|
||||
@@ -445,7 +437,6 @@
|
||||
</div>
|
||||
|
||||
<FlowGraph
|
||||
success={isSuccess(job?.['success'])}
|
||||
flowModuleStates={localFlowModuleStates}
|
||||
on:click={(e) => {
|
||||
if (e.detail.id) {
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
import DynamicInputHelpBox from './flows/content/DynamicInputHelpBox.svelte'
|
||||
import type { PropPickerWrapperContext } from './flows/propPicker/PropPickerWrapper.svelte'
|
||||
import { codeToStaticTemplate, getDefaultExpr, isCodeInjection } from './flows/utils'
|
||||
import SimpleEditor from './SimpleEditor.svelte'
|
||||
import { Button, ToggleButton, ToggleButtonGroup } from './common'
|
||||
import { faCode } from '@fortawesome/free-solid-svg-icons'
|
||||
import type VariableEditor from './VariableEditor.svelte'
|
||||
@@ -17,6 +16,7 @@
|
||||
import TemplateEditor from './TemplateEditor.svelte'
|
||||
import Tooltip from './Tooltip.svelte'
|
||||
import { setInputCat as computeInputCat } from '$lib/utils'
|
||||
import JavascriptEditor from './JavascriptEditor.svelte'
|
||||
|
||||
export let schema: Schema
|
||||
export let arg: InputTransform | any
|
||||
@@ -29,7 +29,7 @@
|
||||
export let itemPicker: ItemPicker | undefined = undefined
|
||||
export let noDynamicToggle = false
|
||||
|
||||
let monaco: SimpleEditor | undefined = undefined
|
||||
let monaco: JavascriptEditor | undefined = undefined
|
||||
let monacoTemplate: TemplateEditor | undefined = undefined
|
||||
let argInput: ArgInput | undefined = undefined
|
||||
|
||||
@@ -284,7 +284,7 @@
|
||||
/>
|
||||
{:else if arg.expr != undefined}
|
||||
<div class="border rounded mt-2 border-gray-300">
|
||||
<SimpleEditor
|
||||
<JavascriptEditor
|
||||
bind:this={monaco}
|
||||
bind:code={arg.expr}
|
||||
{extraLib}
|
||||
|
||||
212
frontend/src/lib/components/JavascriptEditor.svelte
Normal file
212
frontend/src/lib/components/JavascriptEditor.svelte
Normal file
@@ -0,0 +1,212 @@
|
||||
<script lang="ts">
|
||||
import { browser, dev } from '$app/environment'
|
||||
|
||||
import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'
|
||||
|
||||
import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker'
|
||||
|
||||
import { createEventDispatcher, onDestroy, onMount } from 'svelte'
|
||||
import { createHash, editorConfig, langToExt, updateOptions } from '$lib/editorUtils'
|
||||
import { languages, editor as meditor, KeyCode, KeyMod, Uri as mUri } from 'monaco-editor'
|
||||
|
||||
import libStdContent from '$lib/es5.d.ts.txt?raw'
|
||||
|
||||
meditor.defineTheme('myTheme', {
|
||||
base: 'vs',
|
||||
inherit: true,
|
||||
rules: [],
|
||||
colors: {
|
||||
'editorLineNumber.foreground': '#999',
|
||||
'editorGutter.background': '#F9FAFB'
|
||||
}
|
||||
})
|
||||
meditor.setTheme('myTheme')
|
||||
|
||||
languages.typescript.javascriptDefaults.setCompilerOptions({
|
||||
target: languages.typescript.ScriptTarget.Latest,
|
||||
allowNonTsExtensions: true,
|
||||
noLib: true
|
||||
})
|
||||
|
||||
let divEl: HTMLDivElement | null = null
|
||||
let editor: meditor.IStandaloneCodeEditor
|
||||
let model: meditor.ITextModel
|
||||
|
||||
export let lang: string
|
||||
export let code: string = ''
|
||||
export let hash: string = createHash()
|
||||
export let cmdEnterAction: (() => void) | undefined = undefined
|
||||
export let formatAction: (() => void) | undefined = undefined
|
||||
export let automaticLayout = true
|
||||
export let extraLib: string = ''
|
||||
export let shouldBindKey: boolean = true
|
||||
export let autoHeight = false
|
||||
export let fixedOverflowWidgets = true
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const uri = `file:///${hash}.${langToExt(lang)}`
|
||||
|
||||
if (browser) {
|
||||
if (!dev) {
|
||||
// @ts-ignore
|
||||
self.MonacoEnvironment = {
|
||||
getWorker: function (_moduleId: any, label: string) {
|
||||
if (label === 'typescript' || label === 'javascript') {
|
||||
return new tsWorker()
|
||||
} else {
|
||||
return new editorWorker()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getCode(): string {
|
||||
return editor?.getValue() ?? ''
|
||||
}
|
||||
|
||||
export function insertAtCursor(code: string): void {
|
||||
if (editor) {
|
||||
editor.trigger('keyboard', 'type', { text: code })
|
||||
}
|
||||
}
|
||||
|
||||
export function setCode(ncode: string): void {
|
||||
code = ncode
|
||||
if (editor) {
|
||||
editor.setValue(ncode)
|
||||
}
|
||||
}
|
||||
|
||||
function format() {
|
||||
if (editor) {
|
||||
code = getCode()
|
||||
editor.getAction('editor.action.formatDocument').run()
|
||||
if (formatAction) {
|
||||
formatAction()
|
||||
code = getCode()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let width = 0
|
||||
async function loadMonaco() {
|
||||
if (dev) {
|
||||
let { buildWorkerDefinition } = await import('monaco-editor-workers')
|
||||
buildWorkerDefinition(
|
||||
'../../../node_modules/monaco-editor-workers/dist/workers',
|
||||
import.meta.url,
|
||||
false
|
||||
)
|
||||
}
|
||||
model = meditor.createModel(code, lang, mUri.parse(uri))
|
||||
|
||||
model.updateOptions(updateOptions)
|
||||
|
||||
editor = meditor.create(
|
||||
divEl as HTMLDivElement,
|
||||
editorConfig(model, code, lang, automaticLayout, fixedOverflowWidgets)
|
||||
)
|
||||
|
||||
let timeoutModel: NodeJS.Timeout | undefined = undefined
|
||||
editor.onDidChangeModelContent((event) => {
|
||||
timeoutModel && clearTimeout(timeoutModel)
|
||||
timeoutModel = setTimeout(() => {
|
||||
code = getCode()
|
||||
dispatch('change', { code })
|
||||
}, 200)
|
||||
})
|
||||
|
||||
editor.onDidFocusEditorText(() => {
|
||||
dispatch('focus')
|
||||
|
||||
editor.addCommand(KeyMod.CtrlCmd | KeyCode.KeyS, function () {
|
||||
code = getCode()
|
||||
shouldBindKey && format && format()
|
||||
})
|
||||
})
|
||||
|
||||
if (autoHeight) {
|
||||
let ignoreEvent = false
|
||||
const updateHeight = () => {
|
||||
const contentHeight = Math.min(1000, editor.getContentHeight())
|
||||
if (divEl) {
|
||||
divEl.style.height = `${contentHeight}px`
|
||||
}
|
||||
try {
|
||||
ignoreEvent = true
|
||||
editor.layout({ width, height: contentHeight })
|
||||
} finally {
|
||||
ignoreEvent = false
|
||||
}
|
||||
}
|
||||
editor.onDidContentSizeChange(updateHeight)
|
||||
updateHeight()
|
||||
}
|
||||
|
||||
editor.onDidFocusEditorText(() => {
|
||||
editor.addCommand(KeyMod.CtrlCmd | KeyCode.KeyS, function () {
|
||||
code = getCode()
|
||||
shouldBindKey && format && format()
|
||||
})
|
||||
|
||||
editor.addCommand(KeyMod.CtrlCmd | KeyCode.Enter, function () {
|
||||
code = getCode()
|
||||
shouldBindKey && cmdEnterAction && cmdEnterAction()
|
||||
})
|
||||
dispatch('focus')
|
||||
})
|
||||
|
||||
editor.onDidBlurEditorText(() => {
|
||||
code = getCode()
|
||||
dispatch('blur')
|
||||
})
|
||||
|
||||
if (lang == 'javascript') {
|
||||
const stdLib = { content: libStdContent, filePath: 'es5.d.ts' }
|
||||
if (extraLib != '') {
|
||||
languages.typescript.javascriptDefaults.setExtraLibs([
|
||||
{
|
||||
content: extraLib,
|
||||
filePath: 'windmill.d.ts'
|
||||
},
|
||||
stdLib
|
||||
])
|
||||
} else {
|
||||
languages.typescript.javascriptDefaults.setExtraLibs([stdLib])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
if (browser) {
|
||||
loadMonaco()
|
||||
}
|
||||
})
|
||||
|
||||
onDestroy(() => {
|
||||
try {
|
||||
model && model.dispose()
|
||||
editor && editor.dispose()
|
||||
} catch (err) {}
|
||||
})
|
||||
</script>
|
||||
|
||||
<div bind:this={divEl} class="{$$props.class} editor" bind:clientWidth={width} />
|
||||
|
||||
<style>
|
||||
.editor {
|
||||
@apply rounded-lg p-0;
|
||||
}
|
||||
|
||||
.small-editor {
|
||||
/* stylelint-disable-next-line unit-allowed-list */
|
||||
height: 26vh;
|
||||
}
|
||||
|
||||
.few-lines-editor {
|
||||
/* stylelint-disable-next-line unit-allowed-list */
|
||||
height: 100px;
|
||||
}
|
||||
</style>
|
||||
@@ -96,7 +96,7 @@
|
||||
<input
|
||||
type="text"
|
||||
bind:value={summary}
|
||||
placeholder="Short summary to be displayed when listed"
|
||||
placeholder="A short summary displayed when it is listed"
|
||||
disabled={!own}
|
||||
/>
|
||||
|
||||
|
||||
@@ -178,8 +178,8 @@
|
||||
/>
|
||||
<div class="ml-2">
|
||||
<Tooltip>
|
||||
Arguments can be edited either using the wizard, or by editing their JSON Schema,
|
||||
<a href="https://docs.windmill.dev/docs/reference/#script-parameters-to-json-schema">see docs</a>
|
||||
Arguments can be edited either using the wizard, or by editing their json-schema
|
||||
<a href="https://docs.windmill.dev/docs/reference/script_arguments_reference">docs</a>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,11 +7,9 @@
|
||||
import { slide } from 'svelte/transition'
|
||||
import ArgInput from './ArgInput.svelte'
|
||||
import { Button } from './common'
|
||||
import InputTransformForm from './InputTransformForm.svelte'
|
||||
import ItemPicker from './ItemPicker.svelte'
|
||||
import VariableEditor from './VariableEditor.svelte'
|
||||
|
||||
export let inputTransform = false
|
||||
export let schema: Schema
|
||||
export let args: Record<string, InputTransform | any> = {}
|
||||
export let disabledArgs: string[] = []
|
||||
@@ -19,16 +17,13 @@
|
||||
|
||||
export let editableSchema = false
|
||||
export let isValid: boolean = true
|
||||
export let extraLib: string = 'missing extraLib'
|
||||
export let autofocus = false
|
||||
export let previousModuleId: string | undefined = undefined
|
||||
|
||||
export let shouldHideNoInputs: boolean = false
|
||||
export let compact = false
|
||||
export let password: string | undefined = undefined
|
||||
export let noVariablePicker = false
|
||||
export let filter: string[] | undefined = undefined
|
||||
export let noDynamicToggle = false
|
||||
export let flexWrap = false
|
||||
export let noDelete = false
|
||||
|
||||
@@ -71,20 +66,7 @@
|
||||
{#each keys as argName, i (argName)}
|
||||
{#if !filter || filter.includes(argName)}
|
||||
<div transition:slide|local>
|
||||
{#if inputTransform}
|
||||
<InputTransformForm
|
||||
{previousModuleId}
|
||||
bind:arg={args[argName]}
|
||||
bind:schema
|
||||
bind:argName
|
||||
bind:inputCheck={inputCheck[argName]}
|
||||
bind:extraLib
|
||||
{variableEditor}
|
||||
{itemPicker}
|
||||
bind:pickForField
|
||||
{noDynamicToggle}
|
||||
/>
|
||||
{:else if typeof args == 'object'}
|
||||
{#if typeof args == 'object'}
|
||||
<ArgInput
|
||||
autofocus={i == 0 && autofocus}
|
||||
label={argName}
|
||||
@@ -125,11 +107,7 @@
|
||||
bind:this={itemPicker}
|
||||
pickCallback={(path, _) => {
|
||||
if (pickForField) {
|
||||
if (inputTransform) {
|
||||
args[pickForField].value = '$var:' + path
|
||||
} else {
|
||||
args[pickForField] = '$var:' + path
|
||||
}
|
||||
args[pickForField] = '$var:' + path
|
||||
}
|
||||
}}
|
||||
itemName="Variable"
|
||||
|
||||
120
frontend/src/lib/components/SchemaFormTransform.svelte
Normal file
120
frontend/src/lib/components/SchemaFormTransform.svelte
Normal file
@@ -0,0 +1,120 @@
|
||||
<script lang="ts">
|
||||
import type { Schema } from '$lib/common'
|
||||
import { VariableService, type InputTransform } from '$lib/gen'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { allTrue } from '$lib/utils'
|
||||
import { faPlus } from '@fortawesome/free-solid-svg-icons'
|
||||
import { slide } from 'svelte/transition'
|
||||
import { Button } from './common'
|
||||
import InputTransformForm from './InputTransformForm.svelte'
|
||||
import ItemPicker from './ItemPicker.svelte'
|
||||
import VariableEditor from './VariableEditor.svelte'
|
||||
|
||||
export let schema: Schema
|
||||
export let args: Record<string, InputTransform | any> = {}
|
||||
export let isValid: boolean = true
|
||||
export let extraLib: string = 'missing extraLib'
|
||||
export let previousModuleId: string | undefined = undefined
|
||||
|
||||
export let shouldHideNoInputs: boolean = false
|
||||
export let noVariablePicker = false
|
||||
export let filter: string[] | undefined = undefined
|
||||
export let noDynamicToggle = false
|
||||
export let flexWrap = false
|
||||
export let noDelete = false
|
||||
|
||||
let clazz: string = ''
|
||||
export { clazz as class }
|
||||
|
||||
let inputCheck: { [id: string]: boolean } = {}
|
||||
$: isValid = allTrue(inputCheck) ?? false
|
||||
|
||||
$: if (args == undefined || typeof args !== 'object') {
|
||||
args = {}
|
||||
}
|
||||
|
||||
function removeExtraKey() {
|
||||
Object.keys(args ?? {}).forEach((key) => {
|
||||
if (!keys.includes(key)) {
|
||||
delete args[key]
|
||||
delete inputCheck[key]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let pickForField: string | undefined
|
||||
let itemPicker: ItemPicker | undefined = undefined
|
||||
let variableEditor: VariableEditor | undefined = undefined
|
||||
let keys: string[] = []
|
||||
$: {
|
||||
let lkeys = Object.keys(schema?.properties ?? {})
|
||||
if (schema?.properties && JSON.stringify(lkeys) != JSON.stringify(keys)) {
|
||||
keys = lkeys
|
||||
if (!noDelete) {
|
||||
removeExtraKey()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="w-full {clazz} {flexWrap ? 'flex flex-row flex-wrap gap-x-6 gap-y-2' : ''}">
|
||||
{#if keys.length > 0}
|
||||
{#each keys as argName, i (argName)}
|
||||
{#if !filter || filter.includes(argName)}
|
||||
<div transition:slide|local>
|
||||
<InputTransformForm
|
||||
{previousModuleId}
|
||||
bind:arg={args[argName]}
|
||||
bind:schema
|
||||
bind:argName
|
||||
bind:inputCheck={inputCheck[argName]}
|
||||
bind:extraLib
|
||||
{variableEditor}
|
||||
{itemPicker}
|
||||
bind:pickForField
|
||||
{noDynamicToggle}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
{:else if !shouldHideNoInputs}
|
||||
<div class="text-gray-500 text-sm">No inputs</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if !noVariablePicker}
|
||||
<ItemPicker
|
||||
bind:this={itemPicker}
|
||||
pickCallback={(path, _) => {
|
||||
if (pickForField) {
|
||||
args[pickForField].value = '$var:' + path
|
||||
}
|
||||
}}
|
||||
itemName="Variable"
|
||||
extraField="path"
|
||||
loadItems={async () =>
|
||||
(await VariableService.listVariable({ workspace: $workspaceStore ?? '' })).map((x) => ({
|
||||
name: x.path,
|
||||
...x
|
||||
}))}
|
||||
>
|
||||
<div
|
||||
slot="submission"
|
||||
class="flex flex-row-reverse w-full bg-white border-t border-gray-200 rounded-bl-lg rounded-br-lg"
|
||||
>
|
||||
<Button
|
||||
variant="border"
|
||||
color="blue"
|
||||
size="sm"
|
||||
startIcon={{ icon: faPlus }}
|
||||
on:click={() => {
|
||||
variableEditor?.initNew?.()
|
||||
}}
|
||||
>
|
||||
New variable
|
||||
</Button>
|
||||
</div>
|
||||
</ItemPicker>
|
||||
|
||||
<VariableEditor bind:this={variableEditor} />
|
||||
{/if}
|
||||
@@ -213,7 +213,7 @@
|
||||
type="text"
|
||||
bind:this={summaryC}
|
||||
bind:value={script.summary}
|
||||
placeholder="Short summary to be displayed when listed"
|
||||
placeholder="A short summary of the script displayed when the script is listed"
|
||||
/>
|
||||
<h2 class="border-b pb-1 mt-12 mb-6">Language</h2>
|
||||
<div class="flex flex-row gap-2 flex-wrap">
|
||||
|
||||
@@ -4,15 +4,16 @@
|
||||
import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'
|
||||
import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker'
|
||||
import yamlWorker from 'monaco-yaml/yaml.worker?worker'
|
||||
import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker'
|
||||
|
||||
import { buildWorkerDefinition } from 'monaco-editor-workers'
|
||||
|
||||
import { createEventDispatcher, onDestroy, onMount } from 'svelte'
|
||||
import { createHash, editorConfig, langToExt, updateOptions } from '$lib/editorUtils'
|
||||
import { languages, editor as meditor, KeyCode, KeyMod, Uri as mUri } from 'monaco-editor'
|
||||
|
||||
import libStdContent from '$lib/es5.d.ts.txt?raw'
|
||||
import {
|
||||
languages,
|
||||
editor as meditor,
|
||||
KeyCode,
|
||||
KeyMod,
|
||||
Uri as mUri
|
||||
} from 'monaco-editor/esm/vs/editor/editor.main'
|
||||
|
||||
meditor.defineTheme('myTheme', {
|
||||
base: 'vs',
|
||||
@@ -25,12 +26,6 @@
|
||||
})
|
||||
meditor.setTheme('myTheme')
|
||||
|
||||
languages.typescript.javascriptDefaults.setCompilerOptions({
|
||||
target: languages.typescript.ScriptTarget.Latest,
|
||||
allowNonTsExtensions: true,
|
||||
noLib: true
|
||||
})
|
||||
|
||||
languages.json.jsonDefaults.setDiagnosticsOptions({
|
||||
validate: true,
|
||||
allowComments: false,
|
||||
@@ -48,7 +43,6 @@
|
||||
export let cmdEnterAction: (() => void) | undefined = undefined
|
||||
export let formatAction: (() => void) | undefined = undefined
|
||||
export let automaticLayout = true
|
||||
export let extraLib: string = ''
|
||||
export let shouldBindKey: boolean = true
|
||||
export let autoHeight = false
|
||||
export let fixedOverflowWidgets = true
|
||||
@@ -58,13 +52,7 @@
|
||||
const uri = `file:///${hash}.${langToExt(lang)}`
|
||||
|
||||
if (browser) {
|
||||
if (dev) {
|
||||
buildWorkerDefinition(
|
||||
'../../../node_modules/monaco-editor-workers/dist/workers',
|
||||
import.meta.url,
|
||||
false
|
||||
)
|
||||
} else {
|
||||
if (!dev) {
|
||||
// @ts-ignore
|
||||
self.MonacoEnvironment = {
|
||||
getWorker: function (_moduleId: any, label: string) {
|
||||
@@ -72,8 +60,6 @@
|
||||
return new jsonWorker()
|
||||
} else if (label === 'yaml') {
|
||||
return new yamlWorker()
|
||||
} else if (label === 'typescript' || label === 'javascript') {
|
||||
return new tsWorker()
|
||||
} else {
|
||||
return new editorWorker()
|
||||
}
|
||||
@@ -174,21 +160,6 @@
|
||||
code = getCode()
|
||||
dispatch('blur')
|
||||
})
|
||||
|
||||
if (lang == 'javascript') {
|
||||
const stdLib = { content: libStdContent, filePath: 'es5.d.ts' }
|
||||
if (extraLib != '') {
|
||||
languages.typescript.javascriptDefaults.setExtraLibs([
|
||||
{
|
||||
content: extraLib,
|
||||
filePath: 'windmill.d.ts'
|
||||
},
|
||||
stdLib
|
||||
])
|
||||
} else {
|
||||
languages.typescript.javascriptDefaults.setExtraLibs([stdLib])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
|
||||
@@ -12,19 +12,24 @@
|
||||
EditorMode
|
||||
} from '../types'
|
||||
import GridEditor from './GridEditor.svelte'
|
||||
import { classNames } from '$lib/utils'
|
||||
import { classNames } from '../../../utils'
|
||||
import type { Policy } from '$lib/gen'
|
||||
import Button from '../../common/button/Button.svelte'
|
||||
import { Unlock } from 'lucide-svelte'
|
||||
|
||||
export let app: App
|
||||
export let appPath: string
|
||||
export let breakpoint: Writable<EditorBreakpoint>
|
||||
export let policy: Policy
|
||||
export let summary: string
|
||||
export let workspace: string
|
||||
export let isEditor: boolean
|
||||
export let context: Record<string, any>
|
||||
export let app: App = {
|
||||
grid: [],
|
||||
fullscreen: false,
|
||||
unusedInlineScripts: [],
|
||||
hiddenInlineScripts: []
|
||||
}
|
||||
export let appPath: string = ''
|
||||
export let breakpoint: Writable<EditorBreakpoint> = writable('lg')
|
||||
export let policy: Policy = {}
|
||||
export let summary: string = ''
|
||||
export let workspace: string = 'workspace'
|
||||
export let isEditor: boolean = false
|
||||
export let context: Record<string, any> = {}
|
||||
export let noBackend: boolean = false
|
||||
export let isLocked = false
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppEditorContext } from '$lib/components/apps/types'
|
||||
import SimpleEditor from '$lib/components/SimpleEditor.svelte'
|
||||
import { buildExtraLib } from '$lib/components/apps/utils'
|
||||
import JavascriptEditor from '$lib/components/JavascriptEditor.svelte'
|
||||
|
||||
export let componentInput: EvalAppInput | undefined
|
||||
export let id: string
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
{#if componentInput?.type === 'eval'}
|
||||
<div class="border border-gray-300 ">
|
||||
<SimpleEditor
|
||||
<JavascriptEditor
|
||||
lang="javascript"
|
||||
bind:code={componentInput.expr}
|
||||
shouldBindKey={false}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { classNames } from '$lib/utils'
|
||||
import { classNames } from '../../../utils'
|
||||
import Icon from 'svelte-awesome'
|
||||
import { ButtonType } from './model'
|
||||
import { goto } from '$app/navigation'
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
let pendingJson: string
|
||||
|
||||
async function importJson() {
|
||||
$importFlowStore = JSON.parse(pendingJson)
|
||||
await goto('/flows/add')
|
||||
$importFlowStore = JSON.parse(pendingJson)
|
||||
drawer?.closeDrawer?.()
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
import { getContext } from 'svelte'
|
||||
import { flowStore } from '../flowStore'
|
||||
import PropPickerWrapper from '../propPicker/PropPickerWrapper.svelte'
|
||||
import SimpleEditor from '$lib/components/SimpleEditor.svelte'
|
||||
|
||||
import { flowStateStore } from '../flowState'
|
||||
import { getStepPropPicker } from '../previousResults'
|
||||
import type { FlowEditorContext } from '../types'
|
||||
import JavascriptEditor from '$lib/components/JavascriptEditor.svelte'
|
||||
|
||||
export let branch: {
|
||||
summary?: string
|
||||
@@ -18,7 +18,7 @@
|
||||
export let previousModule: FlowModule | undefined
|
||||
|
||||
const { previewArgs } = getContext<FlowEditorContext>('FlowEditorContext')
|
||||
let editor: SimpleEditor | undefined = undefined
|
||||
let editor: JavascriptEditor | undefined = undefined
|
||||
|
||||
$: stepPropPicker = getStepPropPicker(
|
||||
$flowStateStore,
|
||||
@@ -40,7 +40,7 @@
|
||||
}}
|
||||
>
|
||||
<div class="border border-gray-400">
|
||||
<SimpleEditor
|
||||
<JavascriptEditor
|
||||
bind:this={editor}
|
||||
lang="javascript"
|
||||
bind:code={branch.expr}
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
import FlowCard from '../common/FlowCard.svelte'
|
||||
import { Alert, Badge } from '$lib/components/common'
|
||||
import type { FlowModule, FlowModuleValue, InputTransform, PathScript, RawScript } from '$lib/gen'
|
||||
import SchemaForm from '$lib/components/SchemaForm.svelte'
|
||||
import { flowStateStore } from '../flowState'
|
||||
import { setContext } from 'svelte'
|
||||
import type { PropPickerWrapperContext } from '../propPicker/PropPickerWrapper.svelte'
|
||||
import { writable } from 'svelte/store'
|
||||
import { slide } from 'svelte/transition'
|
||||
import Toggle from '../../Toggle.svelte'
|
||||
import SchemaFormTransform from '$lib/components/SchemaFormTransform.svelte'
|
||||
|
||||
let hideOptional = false
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
] as [Record<string, InputTransform>, string[], FlowModule]
|
||||
)
|
||||
.filter(([i, f, m]) => f.length > 0)
|
||||
|
||||
|
||||
setContext<PropPickerWrapperContext>('PropPickerWrapper', {
|
||||
focusProp: () => {},
|
||||
propPickerConfig: writable(undefined),
|
||||
@@ -47,11 +47,7 @@
|
||||
|
||||
<div class="min-h-full">
|
||||
<FlowCard title="All Static Inputs">
|
||||
<Toggle
|
||||
slot="header"
|
||||
bind:checked={hideOptional}
|
||||
options={{left: 'Hide optional inputs'}}
|
||||
/>
|
||||
<Toggle slot="header" bind:checked={hideOptional} options={{ left: 'Hide optional inputs' }} />
|
||||
<div class="min-h-full flex-1">
|
||||
<Alert type="info" title="Static Inputs" class="m-4"
|
||||
>This page centralizes the static inputs of every steps. It is akin to a file containing all
|
||||
@@ -80,9 +76,8 @@
|
||||
<Badge large color="indigo">{m.id}</Badge>
|
||||
</h2>
|
||||
|
||||
<SchemaForm
|
||||
<SchemaFormTransform
|
||||
noDynamicToggle
|
||||
inputTransform
|
||||
{filter}
|
||||
class="mt-2"
|
||||
schema={$flowStateStore[m.id]?.schema ?? {}}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
import FlowCard from '../common/FlowCard.svelte'
|
||||
import type { FlowEditorContext } from '../types'
|
||||
import Toggle from '$lib/components/Toggle.svelte'
|
||||
import SimpleEditor from '$lib/components/SimpleEditor.svelte'
|
||||
import Tooltip from '$lib/components/Tooltip.svelte'
|
||||
import PropPickerWrapper from '../propPicker/PropPickerWrapper.svelte'
|
||||
import FlowModuleEarlyStop from './FlowModuleEarlyStop.svelte'
|
||||
@@ -16,6 +15,7 @@
|
||||
import { flowStateStore } from '../flowState'
|
||||
import { flowStore } from '../flowStore'
|
||||
import FlowModuleSleep from './FlowModuleSleep.svelte'
|
||||
import JavascriptEditor from '$lib/components/JavascriptEditor.svelte'
|
||||
|
||||
const { previewArgs } = getContext<FlowEditorContext>('FlowEditorContext')
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
export let parentModule: FlowModule | undefined
|
||||
export let previousModule: FlowModule | undefined
|
||||
|
||||
let editor: SimpleEditor | undefined = undefined
|
||||
let editor: JavascriptEditor | undefined = undefined
|
||||
let selected: string = 'early-stop'
|
||||
|
||||
$: stepPropPicker = getStepPropPicker(
|
||||
@@ -50,7 +50,7 @@
|
||||
Iterator expression
|
||||
<Tooltip>
|
||||
List to iterate over. For more information see the
|
||||
<a href="https://docs.windmill.dev/docs/getting_started/flows_quickstart">docs.</a>
|
||||
<a href="https://docs.windmill.dev/docs/getting_started/flows#for-loops">docs.</a>
|
||||
</Tooltip>
|
||||
</div>
|
||||
{#if mod.value.iterator.type == 'javascript'}
|
||||
@@ -62,7 +62,7 @@
|
||||
editor?.insertAtCursor(detail)
|
||||
}}
|
||||
>
|
||||
<SimpleEditor
|
||||
<JavascriptEditor
|
||||
bind:this={editor}
|
||||
lang="javascript"
|
||||
bind:code={mod.value.iterator.expr}
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
import ModulePreview from '$lib/components/ModulePreview.svelte'
|
||||
import { createScriptFromInlineScript, fork } from '$lib/components/flows/flowStateUtils'
|
||||
import { flowStore } from '$lib/components/flows/flowStore'
|
||||
import SchemaForm from '$lib/components/SchemaForm.svelte'
|
||||
import { RawScript, type FlowModule, type PathFlow, type PathScript } from '$lib/gen'
|
||||
import FlowCard from '../common/FlowCard.svelte'
|
||||
import FlowModuleHeader from './FlowModuleHeader.svelte'
|
||||
@@ -27,6 +26,7 @@
|
||||
import Alert from '$lib/components/common/alert/Alert.svelte'
|
||||
import FlowModuleSleep from './FlowModuleSleep.svelte'
|
||||
import FlowPathViewer from './FlowPathViewer.svelte'
|
||||
import SchemaFormTransform from '$lib/components/SchemaFormTransform.svelte'
|
||||
|
||||
const { selectedId, previewArgs } = getContext<FlowEditorContext>('FlowEditorContext')
|
||||
|
||||
@@ -211,9 +211,8 @@
|
||||
pickableProperties={stepPropPicker.pickableProperties}
|
||||
error={failureModule}
|
||||
>
|
||||
<SchemaForm
|
||||
<SchemaFormTransform
|
||||
schema={$flowStateStore[$selectedId]?.schema ?? {}}
|
||||
inputTransform={true}
|
||||
previousModuleId={previousModule?.id}
|
||||
bind:args={value.input_transforms}
|
||||
bind:extraLib={stepPropPicker.extraLib}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
<script lang="ts">
|
||||
import SimpleEditor from '$lib/components/SimpleEditor.svelte'
|
||||
import Toggle from '$lib/components/Toggle.svelte'
|
||||
import PropPickerWrapper from '$lib/components/flows/propPicker/PropPickerWrapper.svelte'
|
||||
import type { FlowModule } from '$lib/gen'
|
||||
import { flowStateStore } from '../flowState'
|
||||
import Tooltip from '$lib/components/Tooltip.svelte'
|
||||
import { NEVER_TESTED_THIS_FAR } from '../utils'
|
||||
import JavascriptEditor from '$lib/components/JavascriptEditor.svelte'
|
||||
|
||||
export let flowModule: FlowModule
|
||||
|
||||
let editor: SimpleEditor | undefined = undefined
|
||||
let editor: JavascriptEditor | undefined = undefined
|
||||
|
||||
$: isStopAfterIfEnabled = Boolean(flowModule.stop_after_if)
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
editor?.insertAtCursor(detail)
|
||||
}}
|
||||
>
|
||||
<SimpleEditor
|
||||
<JavascriptEditor
|
||||
bind:this={editor}
|
||||
lang="javascript"
|
||||
bind:code={flowModule.stop_after_if.expr}
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
<input
|
||||
type="text"
|
||||
bind:value={$flowStore.summary}
|
||||
placeholder="Short summary to be displayed when listed"
|
||||
placeholder="A short summary of the flow displayed when the flow is listed"
|
||||
id="flow-summary"
|
||||
/>
|
||||
</label>
|
||||
@@ -69,7 +69,7 @@
|
||||
class="text-sm"
|
||||
id="inp"
|
||||
bind:value={$flowStore.description}
|
||||
placeholder="What this flow does and how to use it."
|
||||
placeholder="A description to help users understand what this flow does and how to use it."
|
||||
rows="3"
|
||||
/>
|
||||
</span>
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { charsToNumber, numberToChars } from '../flows/utils'
|
||||
|
||||
export let success: boolean | undefined = undefined
|
||||
export let modules: FlowModule[] | undefined = []
|
||||
export let failureModule: FlowModule | undefined = undefined
|
||||
export let minHeight: number = 0
|
||||
@@ -120,13 +119,7 @@
|
||||
} else if (type === 'forloopflow') {
|
||||
return flowModuleToLoop(module.value.modules, module, parent)
|
||||
} else if (type === 'branchone') {
|
||||
const branches = [
|
||||
{ summary: 'Default branch', modules: module.value.default },
|
||||
...module.value.branches.map((b, i) => ({
|
||||
summary: b.summary ?? 'Branch ' + (i + 1),
|
||||
modules: b.modules
|
||||
}))
|
||||
]
|
||||
const branches = [module.value.default, ...module.value.branches.map((b) => b.modules)]
|
||||
return flowModuleToBranch(
|
||||
module,
|
||||
branches,
|
||||
@@ -135,10 +128,7 @@
|
||||
insideLoop
|
||||
)
|
||||
} else if (type === 'branchall') {
|
||||
const branches = module.value.branches.map((b, i) => ({
|
||||
summary: b.summary ?? `Branch ${i + 1}`,
|
||||
modules: b.modules
|
||||
}))
|
||||
const branches = module.value.branches.map((b) => b.modules)
|
||||
return flowModuleToBranch(module, branches, [], parent, insideLoop)
|
||||
} else if (type === 'flow') {
|
||||
return flowModuleToNode(
|
||||
@@ -200,17 +190,6 @@
|
||||
return '#fff'
|
||||
}
|
||||
}
|
||||
|
||||
function getResultColor(): string | undefined {
|
||||
switch (success) {
|
||||
case true:
|
||||
return getStateColor(FlowStatusModule.type.SUCCESS)
|
||||
case false:
|
||||
return getStateColor(FlowStatusModule.type.FAILURE)
|
||||
default:
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
function flowModuleToNode(
|
||||
parentIds: string[],
|
||||
id: string,
|
||||
@@ -318,7 +297,7 @@
|
||||
|
||||
function flowModuleToBranch(
|
||||
module: FlowModule,
|
||||
branches: { summary: string; modules: FlowModule[] }[],
|
||||
branches: FlowModule[][],
|
||||
edgesLabel: string[],
|
||||
parent: string | NestedNodes | undefined = undefined,
|
||||
insideLoop: boolean = false
|
||||
@@ -344,12 +323,18 @@
|
||||
if (branches.length == 0) {
|
||||
branch.items.push([createVirtualNode(branchParent, 'No branches')])
|
||||
}
|
||||
branches.forEach(({ summary, modules }, i) => {
|
||||
branches.forEach((modules, i) => {
|
||||
const items: NestedNodes = []
|
||||
items.push(createVirtualNode(branchParent, summary, edgesLabel[i]))
|
||||
if (modules.length) {
|
||||
if (!modules.length) {
|
||||
items.push(createVirtualNode(branchParent, 'Empty branch', edgesLabel[i]))
|
||||
} else {
|
||||
modules.forEach((module, j) => {
|
||||
const item = getConvertedFlowModule(module, items, undefined, insideLoop)
|
||||
const item = getConvertedFlowModule(
|
||||
module,
|
||||
items.length ? items : numberToChars(branch.node.id),
|
||||
j == 0 ? edgesLabel[i] : undefined,
|
||||
insideLoop
|
||||
)
|
||||
item && items.push(item)
|
||||
})
|
||||
}
|
||||
@@ -446,7 +431,7 @@
|
||||
function createVirtualNode(
|
||||
parentIds: string[],
|
||||
label: string,
|
||||
edgeLabel?: string,
|
||||
edgesLabel?: string,
|
||||
offset?: number
|
||||
): Node {
|
||||
const id = -idGenerator.next().value - 1 + (offset ?? 0)
|
||||
@@ -463,15 +448,14 @@
|
||||
width: NODE.width,
|
||||
height: NODE.height,
|
||||
borderColor: selectedNode == label ? 'black' : '#999',
|
||||
bgColor: label == 'Result' ? getResultColor() : selectedNode == label ? '#f5f5f5' : '#d4e4ff',
|
||||
bgColor: selectedNode == label ? '#f5f5f5' : '#d4e4ff',
|
||||
parentIds,
|
||||
clickCallback: (node) => {
|
||||
if (!notSelectable) {
|
||||
selectedNode = label
|
||||
}
|
||||
dispatch('click', label)
|
||||
},
|
||||
edgeLabel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
12
frontend/src/lib/fakesvelte.js
Normal file
12
frontend/src/lib/fakesvelte.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import { readable } from 'svelte/store'
|
||||
|
||||
export async function goto(x) {
|
||||
console.error('Goto not implemented')
|
||||
}
|
||||
|
||||
export const browser = true
|
||||
export const dev = true
|
||||
|
||||
export const page = readable({ url: document.URL }, function start(set) {
|
||||
return function stop() {}
|
||||
})
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
UserService,
|
||||
type Flow,
|
||||
type User
|
||||
} from '$lib/gen'
|
||||
} from '$lib/gen/index'
|
||||
import { toast } from '@zerodevx/svelte-toast'
|
||||
import type { Schema, SupportedLanguage } from './common'
|
||||
import { hubScripts, workspaceStore, type UserExt } from './stores'
|
||||
|
||||
@@ -307,7 +307,7 @@
|
||||
>Webhook<Tooltip>
|
||||
Pass the input as a json payload, the token as a Bearer token or as query arg
|
||||
`?token=XXX` and pass as header: 'Content-Type: application/json <a
|
||||
href="https://docs.windmill.dev/docs/core_concepts/webhooks">See docs</a
|
||||
href="https://docs.windmill.dev/docs/getting_started/webhooks">See docs</a
|
||||
></Tooltip
|
||||
></h2
|
||||
>
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
} else {
|
||||
sendUserToast('Missing code or state as query params', true)
|
||||
}
|
||||
goto('/workspace_settings?tab=slack')
|
||||
goto('/workspace_settings')
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -448,7 +448,7 @@
|
||||
<Tooltip>
|
||||
Pass the input as a json payload, the token as a Bearer token or as query arg
|
||||
`?token=XXX` and pass as header: 'Content-Type: application/json'
|
||||
<a href="https://docs.windmill.dev/docs/core_concepts/webhooks" class="text-blue-500">
|
||||
<a href="https://docs.windmill.dev/docs/getting_started/webhooks" class="text-blue-500">
|
||||
See docs
|
||||
</a>
|
||||
</Tooltip>
|
||||
|
||||
59
frontend/vite.embed.config.js
Normal file
59
frontend/vite.embed.config.js
Normal file
@@ -0,0 +1,59 @@
|
||||
// import { sveltekit } from '@sveltejs/kit/vite'
|
||||
import { readFileSync } from 'fs'
|
||||
import { fileURLToPath } from 'url'
|
||||
import { svelte } from '@sveltejs/vite-plugin-svelte'
|
||||
|
||||
const file = fileURLToPath(new URL('package.json', import.meta.url))
|
||||
const json = readFileSync(file, 'utf8')
|
||||
const version = JSON.parse(json)
|
||||
|
||||
/** @type {import('vite').UserConfig} */
|
||||
const config = {
|
||||
server: {
|
||||
port: 3000
|
||||
},
|
||||
preview: {
|
||||
port: 3000
|
||||
},
|
||||
plugins: [svelte()],
|
||||
define: {
|
||||
__pkg__: version
|
||||
},
|
||||
optimizeDeps: {
|
||||
include: ['highlight.js', 'highlight.js/lib/core']
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
path: 'path-browserify',
|
||||
$lib: ['/src/lib'],
|
||||
'$lib/*': ['/src/lib/*'],
|
||||
'$app/navigation': ['/src/lib/fakesvelte.js'],
|
||||
'$app/stores': ['/src/lib/fakesvelte.js'],
|
||||
'$app/environment': ['/src/lib/fakesvelte.js']
|
||||
}
|
||||
},
|
||||
build: {
|
||||
lib: {
|
||||
// Could also be a dictionary or array of multiple entry points
|
||||
entry: ['src/embed.js'],
|
||||
name: 'AppViewer',
|
||||
// the proper extensions will be added
|
||||
fileName: 'appviewer',
|
||||
formats: ['iife']
|
||||
}
|
||||
// rollupOptions: {
|
||||
// external: ['highlight.js'],
|
||||
// input: 'src/embed.js',
|
||||
// output: {
|
||||
// format: 'esm',
|
||||
// name: 'app',
|
||||
// // We output it to public. This way, our svelte kit
|
||||
// // app will also host the web components.
|
||||
// dir: 'build'
|
||||
// // file: 'build/app.js'
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
export default config
|
||||
@@ -2,7 +2,7 @@ DO
|
||||
$do$
|
||||
BEGIN
|
||||
IF EXISTS (
|
||||
select usesuper from pg_user where (usename = CURRENT_USER AND usesuper = 't')) OR (CURRENT_USER = 'cloudsqlsuperuser')
|
||||
select usesuper from pg_user where usename = CURRENT_USER AND usesuper = 't')
|
||||
AND NOT EXISTS (
|
||||
SELECT
|
||||
FROM pg_catalog.pg_roles
|
||||
@@ -35,7 +35,7 @@ $do$;
|
||||
DO
|
||||
$do$
|
||||
BEGIN
|
||||
IF EXISTS (select usesuper from pg_user where usename = CURRENT_USER AND usesuper = 't') OR (CURRENT_USER = 'cloudsqlsuperuser')
|
||||
IF EXISTS (select usesuper from pg_user where usename = CURRENT_USER AND usesuper = 't')
|
||||
AND NOT EXISTS (
|
||||
SELECT
|
||||
FROM pg_catalog.pg_roles
|
||||
|
||||
Reference in New Issue
Block a user