Compare commits

...

29 Commits

Author SHA1 Message Date
Ruben Fiszel
1ea08f1e8f inline script drawer 2023-02-28 15:46:06 +01:00
Ruben Fiszel
d302c92766 Merge branch 'main' into rf/flowFix 2023-02-28 11:18:22 +01:00
Ruben Fiszel
ea2ebfa92e fix compile issue 2023-02-28 11:18:09 +01:00
Ruben Fiszel
128622117d progress 2023-02-28 11:15:56 +01:00
dependabot[bot]
ba856be10d chore(deps): bump docker/metadata-action from 3 to 4 (#1243)
Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 3 to 4.
- [Release notes](https://github.com/docker/metadata-action/releases)
- [Upgrade guide](https://github.com/docker/metadata-action/blob/master/UPGRADE.md)
- [Commits](https://github.com/docker/metadata-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: docker/metadata-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-28 11:02:14 +01:00
dependabot[bot]
333b873ee9 chore(deps): bump docker/build-push-action from 3 to 4 (#1186)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 3 to 4.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-28 11:02:00 +01:00
dependabot[bot]
2785b05064 chore(deps-dev): bump @playwright/test in /frontend (#1244)
Bumps [@playwright/test](https://github.com/Microsoft/playwright) from 1.29.2 to 1.31.1.
- [Release notes](https://github.com/Microsoft/playwright/releases)
- [Commits](https://github.com/Microsoft/playwright/compare/v1.29.2...v1.31.1)

---
updated-dependencies:
- dependency-name: "@playwright/test"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-28 11:01:33 +01:00
Faton Ramadani
a67f10eeb6 fix(frontend): Fix deeply nested move (#1245)
* fix(frontend): Fix deeply nested move

* fix(frontend): update comment
2023-02-28 10:53:51 +01:00
Ruben Fiszel
287b2db22f feat(cli): add autocompletions 2023-02-28 10:32:34 +01:00
Ádám Kovács
a4e4d188ad fix(frontend): Add more app custom css (#1229)
* fix(frontend): Add container custom css

* fix(frontend): Add form custom css

* fix(frontend): Add form button custom css

* feat(frontend): Add css quick reset button

* feat(frontend): Filter component css by usage

* fix(frontend): Save state of unused component display

* fix(frontend): Add pie chart custom css

* fix(frontend): Add bar chart custom css

* fix(frontend): Update vega lite and plotly loading

* fix(frontend): Add html and timeseries custom css

* fix(frontend): Add scatter chart custom css

* fix(frontend): Add table custom css

* fix(frontend): Revert container custom styles

* fix(frontend): Add toggle custom css

* fix(frontend): Add text input custom css

* fix(frontend): Update

* fix(frontend): Remove undefined css customizations
2023-02-28 09:21:56 +01:00
Ruben Fiszel
150ce7f731 Merge branch 'main' into rf/flowFix 2023-02-27 18:54:26 +01:00
Ruben Fiszel
2244e83b9d fix(frontend): invisible subgrids have h-0 + app policies fix 2023-02-27 18:38:22 +01:00
Ruben Fiszel
fae406ee8f update docs 2023-02-27 18:26:59 +01:00
Ruben Fiszel
42d1cd6456 fix(frontend): display currently selected filter even if not in list 2023-02-27 16:20:31 +01:00
Ruben Fiszel
4b64e75bd1 add back all tailwind colors to tailwind config 2023-02-27 15:11:23 +01:00
Ruben Fiszel
51a7eaaeb0 rename counters 2023-02-27 14:52:24 +01:00
Ruben Fiszel
8589b70ccf apps improvements 2023-02-27 14:37:32 +01:00
Ruben Fiszel
0bf6f23c9e fix setup app to use updated version of the CLI 2023-02-27 14:16:57 +01:00
Ruben Fiszel
e56869092a feat(backend): use counter for sleep/execution/pull durations 2023-02-27 12:00:32 +01:00
Ruben Fiszel
6b8758f4a5 chore(main): release 1.70.1 (#1241) 2023-02-27 10:30:41 +01:00
Ruben Fiszel
fbc929ba1b chore(main): release 1.70.1 (#1239)
* chore(main): release 1.70.1

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2023-02-27 10:07:26 +01:00
Faton Ramadani
97602ac6db fix(frontend): Fix inline scripts list (#1240) 2023-02-27 10:06:42 +01:00
Faton Ramadani
8ee9d67f4f fix(frontend): Fix subgrid lock (#1232)
* fix(frontend): Fix subgrid lock

* feat(frontend): restore
2023-02-27 10:00:18 +01:00
Ruben Fiszel
4bf6e753f1 fix findGridItemById 2023-02-27 09:56:17 +01:00
Faton Ramadani
70eab303bd fix(frontend): Disable move in nested subgrid (#1238)
* fix(frontend): Disable move in nested subgrid

* fix(frontend): Disable move in nested subgrid
2023-02-27 09:50:39 +01:00
Ruben Fiszel
c051ffeb42 fix(cli): make cli resilient to systems without openable browsers 2023-02-27 09:48:52 +01:00
Ruben Fiszel
ebb68e5320 chore(main): release 1.70.0 (#1236)
* chore(main): release 1.70.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2023-02-27 09:25:03 +01:00
Ruben Fiszel
04a076f1db fix(cli): bump cli to non broken client 1.69.3 2023-02-27 09:24:18 +01:00
Ruben Fiszel
ebd2e0323e fix stripe checkout 2023-02-27 08:49:36 +01:00
90 changed files with 2309 additions and 488 deletions

View File

@@ -182,7 +182,7 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push privately
uses: docker/build-push-action@v3
uses: docker/build-push-action@v4
if: github.event_name != 'pull_request'
with:
context: .
@@ -222,7 +222,7 @@ jobs:
password: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Build and push privately
uses: docker/build-push-action@v3
uses: docker/build-push-action@v4
if: github.event_name != 'pull_request'
with:
context: .

View File

@@ -65,7 +65,7 @@ jobs:
password: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Build and push publicly
uses: docker/build-push-action@v3
uses: docker/build-push-action@v4
with:
context: "{{defaultContext}}:lsp"
push: true

View File

@@ -25,12 +25,12 @@ jobs:
run: echo "UUID_TAG_APP=$(uuidgen)" >> $GITHUB_ENV
- name: Docker metadata
id: meta
uses: docker/metadata-action@v3
uses: docker/metadata-action@v4
with:
images: registry.uffizzi.com/${{ env.UUID_TAG_APP }}
tags: type=raw,value=60d
- name: Build and Push Image to registry.uffizzi.com ephemeral registry
uses: docker/build-push-action@v2
uses: docker/build-push-action@v4
with:
push: true
context: ./

View File

@@ -1,6 +1,40 @@
# Changelog
## [1.70.1](https://github.com/windmill-labs/windmill/compare/v1.70.0...v1.70.1) (2023-02-27)
### Bug Fixes
* **cli:** make cli resilient to systems without openable browsers ([c051ffe](https://github.com/windmill-labs/windmill/commit/c051ffeb42c1cff609f93da7745036ea722e17d4))
* **frontend:** Disable move in nested subgrid ([#1238](https://github.com/windmill-labs/windmill/issues/1238)) ([70eab30](https://github.com/windmill-labs/windmill/commit/70eab303bd45111ae198d9b710bfd6f9f59e53b0))
* **frontend:** Fix inline scripts list ([#1240](https://github.com/windmill-labs/windmill/issues/1240)) ([97602ac](https://github.com/windmill-labs/windmill/commit/97602ac6db1404d36d160a431ffcea6c0f567a48))
* **frontend:** Fix subgrid lock ([#1232](https://github.com/windmill-labs/windmill/issues/1232)) ([8ee9d67](https://github.com/windmill-labs/windmill/commit/8ee9d67f4faa91446338b41c664ef91913eb8b81))
## [1.70.1](https://github.com/windmill-labs/windmill/compare/v1.70.0...v1.70.1) (2023-02-27)
### Bug Fixes
* **cli:** make cli resilient to systems without openable browsers ([c051ffe](https://github.com/windmill-labs/windmill/commit/c051ffeb42c1cff609f93da7745036ea722e17d4))
* **frontend:** Disable move in nested subgrid ([#1238](https://github.com/windmill-labs/windmill/issues/1238)) ([70eab30](https://github.com/windmill-labs/windmill/commit/70eab303bd45111ae198d9b710bfd6f9f59e53b0))
* **frontend:** Fix subgrid lock ([#1232](https://github.com/windmill-labs/windmill/issues/1232)) ([8ee9d67](https://github.com/windmill-labs/windmill/commit/8ee9d67f4faa91446338b41c664ef91913eb8b81))
## [1.70.0](https://github.com/windmill-labs/windmill/compare/v1.69.3...v1.70.0) (2023-02-27)
### Features
* **apps:** add ag grid ([b690d80](https://github.com/windmill-labs/windmill/commit/b690d801d4aa5695ee558e81d1ed114074dfcb83))
* **frontend:** move to other grid ([#1230](https://github.com/windmill-labs/windmill/issues/1230)) ([104e4ac](https://github.com/windmill-labs/windmill/commit/104e4ac5e790c30e6fb6b27726776693038d4f19))
### Bug Fixes
* app setup and sync now uses 1.69.3 ([d38aff2](https://github.com/windmill-labs/windmill/commit/d38aff2fe228f23eb18c3991392928c064e6aca2))
* **frontend:** Fix duplication ([#1237](https://github.com/windmill-labs/windmill/issues/1237)) ([e87f4fc](https://github.com/windmill-labs/windmill/commit/e87f4fc44b847a573f5acafc0348fbcbfcb2258f))
* **frontend:** fix graph viewer id assignment ([e1f686d](https://github.com/windmill-labs/windmill/commit/e1f686d8508cfc1f73c43be08facc44217ca8de0))
## [1.69.3](https://github.com/windmill-labs/windmill/compare/v1.69.2...v1.69.3) (2023-02-24)

104
backend/Cargo.lock generated
View File

@@ -255,9 +255,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "axum"
version = "0.6.8"
version = "0.6.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bd379e511536bad07447f899300aa526e9bae8e6f66dc5e5ca45d7587b7c1ec"
checksum = "6137c6234afb339e75e764c866e3594900f0211e1315d33779f269bbe2ec6967"
dependencies = [
"async-trait",
"axum-core",
@@ -282,7 +282,7 @@ dependencies = [
"sync_wrapper",
"tokio",
"tower",
"tower-http 0.3.5",
"tower-http",
"tower-layer",
"tower-service",
]
@@ -324,9 +324,9 @@ checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
[[package]]
name = "base64ct"
version = "1.5.3"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf"
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
[[package]]
name = "better_scoped_tls"
@@ -581,9 +581,9 @@ dependencies = [
[[package]]
name = "const-oid"
version = "0.9.1"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cec318a675afcb6a1ea1d4340e2d377e56e47c266f28043ceccbf4412ddfdd3b"
checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913"
[[package]]
name = "const_format"
@@ -619,7 +619,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7efb37c3e1ccb1ff97164ad95ac1606e8ccd35b3fa0a7d99a304c7f4a428cc24"
dependencies = [
"percent-encoding",
"time 0.3.19",
"time 0.3.20",
"version_check",
]
@@ -2798,15 +2798,6 @@ dependencies = [
"memchr",
]
[[package]]
name = "remove_dir_all"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [
"winapi",
]
[[package]]
name = "reqwest"
version = "0.11.14"
@@ -3003,7 +2994,7 @@ dependencies = [
[[package]]
name = "rustpython-ast"
version = "0.2.0"
source = "git+https://github.com/RustPython/RustPython#f51764f8d0c33e8e3b423e8a80dba00926715360"
source = "git+https://github.com/RustPython/RustPython#351d464448607413dc12de6905f1165f8d45af54"
dependencies = [
"num-bigint",
"rustpython-compiler-core",
@@ -3012,7 +3003,7 @@ dependencies = [
[[package]]
name = "rustpython-compiler-core"
version = "0.2.0"
source = "git+https://github.com/RustPython/RustPython#f51764f8d0c33e8e3b423e8a80dba00926715360"
source = "git+https://github.com/RustPython/RustPython#351d464448607413dc12de6905f1165f8d45af54"
dependencies = [
"bincode",
"bitflags",
@@ -3029,7 +3020,7 @@ dependencies = [
[[package]]
name = "rustpython-parser"
version = "0.2.0"
source = "git+https://github.com/RustPython/RustPython#f51764f8d0c33e8e3b423e8a80dba00926715360"
source = "git+https://github.com/RustPython/RustPython#351d464448607413dc12de6905f1165f8d45af54"
dependencies = [
"ahash",
"anyhow",
@@ -3083,9 +3074,9 @@ dependencies = [
[[package]]
name = "schemars"
version = "0.8.11"
version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a5fb6c61f29e723026dc8e923d94c694313212abbecbbe5f55a7748eec5b307"
checksum = "02c613288622e5f0c3fdc5dbd4db1c5fbe752746b1d1a56a0630b78fd00de44f"
dependencies = [
"chrono",
"dyn-clone",
@@ -3097,9 +3088,9 @@ dependencies = [
[[package]]
name = "schemars_derive"
version = "0.8.11"
version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f188d036977451159430f3b8dc82ec76364a42b7e289c2b18a9a18f4470058e9"
checksum = "109da1e6b197438deb6db99952990c7f959572794b80ff93707d55a232545e7c"
dependencies = [
"proc-macro2",
"quote",
@@ -3840,16 +3831,15 @@ checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
[[package]]
name = "tempfile"
version = "3.3.0"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95"
dependencies = [
"cfg-if",
"fastrand",
"libc",
"redox_syscall",
"remove_dir_all",
"winapi",
"rustix",
"windows-sys 0.42.0",
]
[[package]]
@@ -3926,9 +3916,9 @@ dependencies = [
[[package]]
name = "time"
version = "0.3.19"
version = "0.3.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53250a3b3fed8ff8fd988587d8925d26a83ac3845d9e03b220b37f34c2b8d6c2"
checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890"
dependencies = [
"itoa",
"serde",
@@ -3944,9 +3934,9 @@ checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
[[package]]
name = "time-macros"
version = "0.2.7"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a460aeb8de6dcb0f381e1ee05f1cd56fcf5a5f6eb8187ff3d8f0b11078d38b7c"
checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36"
dependencies = [
"time-core",
]
@@ -4151,25 +4141,6 @@ dependencies = [
"tower-service",
]
[[package]]
name = "tower-http"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858"
dependencies = [
"bitflags",
"bytes",
"futures-core",
"futures-util",
"http",
"http-body",
"http-range-header",
"pin-project-lite",
"tower",
"tower-layer",
"tower-service",
]
[[package]]
name = "tower-http"
version = "0.4.0"
@@ -4184,6 +4155,7 @@ dependencies = [
"http-body",
"http-range-header",
"pin-project-lite",
"tower",
"tower-layer",
"tower-service",
"tracing",
@@ -4784,7 +4756,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windmill"
version = "1.69.2"
version = "1.70.1"
dependencies = [
"anyhow",
"axum",
@@ -4811,7 +4783,7 @@ dependencies = [
[[package]]
name = "windmill-api"
version = "1.69.2"
version = "1.70.1"
dependencies = [
"anyhow",
"argon2",
@@ -4844,13 +4816,13 @@ dependencies = [
"sql-builder",
"sqlx",
"tempfile",
"time 0.3.19",
"time 0.3.20",
"tokio",
"tokio-tar",
"tokio-util",
"tower",
"tower-cookies",
"tower-http 0.4.0",
"tower-http",
"tracing",
"tracing-subscriber",
"urlencoding",
@@ -4866,7 +4838,7 @@ dependencies = [
[[package]]
name = "windmill-api-client"
version = "1.69.2"
version = "1.70.1"
dependencies = [
"base64 0.21.0",
"chrono",
@@ -4881,7 +4853,7 @@ dependencies = [
[[package]]
name = "windmill-audit"
version = "1.69.2"
version = "1.70.1"
dependencies = [
"chrono",
"serde",
@@ -4894,7 +4866,7 @@ dependencies = [
[[package]]
name = "windmill-common"
version = "1.69.2"
version = "1.70.1"
dependencies = [
"anyhow",
"axum",
@@ -4919,7 +4891,7 @@ dependencies = [
[[package]]
name = "windmill-parser"
version = "1.69.2"
version = "1.70.1"
dependencies = [
"serde",
"serde_json",
@@ -4927,7 +4899,7 @@ dependencies = [
[[package]]
name = "windmill-parser-bash"
version = "1.69.2"
version = "1.70.1"
dependencies = [
"anyhow",
"itertools",
@@ -4941,7 +4913,7 @@ dependencies = [
[[package]]
name = "windmill-parser-go"
version = "1.69.2"
version = "1.70.1"
dependencies = [
"anyhow",
"itertools",
@@ -4953,7 +4925,7 @@ dependencies = [
[[package]]
name = "windmill-parser-py"
version = "1.69.2"
version = "1.70.1"
dependencies = [
"anyhow",
"itertools",
@@ -4968,7 +4940,7 @@ dependencies = [
[[package]]
name = "windmill-parser-ts"
version = "1.69.2"
version = "1.70.1"
dependencies = [
"anyhow",
"deno_core",
@@ -4982,7 +4954,7 @@ dependencies = [
[[package]]
name = "windmill-queue"
version = "1.69.2"
version = "1.70.1"
dependencies = [
"anyhow",
"chrono",
@@ -5005,7 +4977,7 @@ dependencies = [
[[package]]
name = "windmill-worker"
version = "1.69.2"
version = "1.70.1"
dependencies = [
"anyhow",
"async-recursion",

View File

@@ -1,6 +1,6 @@
[package]
name = "windmill"
version = "1.69.2"
version = "1.70.1"
authors.workspace = true
edition.workspace = true
@@ -19,7 +19,7 @@ members = [
]
[workspace.package]
version = "1.69.2"
version = "1.70.1"
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
edition = "2021"

View File

@@ -0,0 +1 @@
-- Add down migration script here

View File

@@ -0,0 +1 @@
-- Add down migration script here

View File

@@ -0,0 +1,16 @@
-- Add up migration script here
UPDATE script SET content = 'import wmill from "https://deno.land/x/wmill@v1.70.1/main.ts";
export async function main() {
await run(
"workspace", "add", "__automation", "admins", Deno.env.get("BASE_INTERNAL_URL") + "/", "--token", Deno.env.get("WM_TOKEN"));
await run("hub", "pull");
}
async function run(...cmd: string[]) {
console.log("Running \"" + cmd.join('' '') + "\"");
await wmill.parse(cmd);
}', summary = 'Synchronize Hub Resource types with admins workspace',
description = 'Basic administrative script to sync latest resource types from hub to share to every workspace. Recommended to run at least once. On a schedule by default.'
WHERE hash = -28028598712388162 AND workspace_id = 'admins';

View File

@@ -1,7 +1,7 @@
openapi: "3.0.3"
info:
version: 1.69.2
version: 1.70.1
title: Windmill API
contact:

View File

@@ -6,10 +6,10 @@
* LICENSE-AGPL for a copy of the license.
*/
#[cfg(enterprise)]
#[cfg(feature = "enterprise")]
use std::str::FromStr;
#[cfg(enterprise)]
#[cfg(feature = "enterprise")]
use crate::BASE_URL;
use crate::{
apps::AppWithLastVersion,
@@ -20,7 +20,7 @@ use crate::{
utils::require_super_admin,
HTTP_CLIENT,
};
#[cfg(enterprise)]
#[cfg(feature = "enterprise")]
use axum::response::Redirect;
use axum::{
body::StreamBody,
@@ -30,7 +30,7 @@ use axum::{
routing::{delete, get, post},
Json, Router,
};
#[cfg(enterprise)]
#[cfg(feature = "enterprise")]
use stripe::CustomerId;
use windmill_audit::{audit_log, ActionKind};
use windmill_common::{
@@ -63,12 +63,13 @@ pub fn workspaced_service() -> Router {
.route("/tarball", get(tarball_workspace))
.route("/premium_info", get(premium_info));
#[cfg(enterprise)]
let router = {
router
.route("/checkout", get(stripe_checkout))
.route("/billing_portal", get(stripe_portal));
};
#[cfg(feature = "enterprise")]
tracing::info!("stripe enabled");
#[cfg(feature = "enterprise")]
let router = router
.route("/checkout", get(stripe_checkout))
.route("/billing_portal", get(stripe_portal));
router
}
@@ -230,13 +231,13 @@ async fn premium_info(
Ok(Json(row))
}
#[cfg(enterprise)]
#[cfg(feature = "enterprise")]
#[derive(Deserialize)]
struct PlanQuery {
plan: String,
}
#[cfg(enterprise)]
#[cfg(feature = "enterprise")]
async fn stripe_checkout(
authed: Authed,
Path(w_id): Path<String>,
@@ -302,7 +303,7 @@ async fn stripe_checkout(
}
}
#[cfg(enterprise)]
#[cfg(feature = "enterprise")]
async fn stripe_portal(
authed: Authed,
Path(w_id): Path<String>,

View File

@@ -351,7 +351,10 @@ pub async fn push<'c>(
.unwrap_or(false);
if !is_super_admin {
if usage > MAX_FREE_EXECS {
if usage > MAX_FREE_EXECS
&& !matches!(job_payload, JobPayload::Dependencies { .. })
&& !matches!(job_payload, JobPayload::FlowDependencies { .. })
{
return Err(error::Error::BadRequest(format!(
"User {email} has exceeded the free usage limit of {MAX_FREE_EXECS} that applies outside of premium workspaces."
)));
@@ -466,10 +469,10 @@ pub async fn push<'c>(
}
JobPayload::Flow(flow) => {
let value_json = sqlx::query_scalar!(
"SELECT value FROM flow WHERE path = $1 AND workspace_id = $2",
flow,
workspace_id
)
"SELECT value FROM flow WHERE path = $1 AND workspace_id = $2",
flow,
workspace_id
)
.fetch_optional(&mut tx)
.await?
.ok_or_else(|| Error::InternalErr(format!("not found flow at path {:?}", flow)))?;

View File

@@ -383,7 +383,7 @@ lazy_static::lazy_static! {
.unwrap();
static ref WORKER_UPTIME_OPTS: prometheus::Opts = prometheus::opts!(
"worker_uptime",
"Total number of milliseconds since the worker has started"
"Total number of seconds since the worker has started"
);
static ref TIMEOUT: u16 = std::env::var("TIMEOUT")
@@ -445,15 +445,11 @@ pub async fn run_worker(
insert_initial_ping(worker_instance, &worker_name, ip, db).await;
let uptime_metric = prometheus::register_int_counter!(WORKER_UPTIME_OPTS
let uptime_metric = prometheus::register_counter!(WORKER_UPTIME_OPTS
.clone()
.const_label("name", &worker_name))
.unwrap();
uptime_metric.inc_by(
((Instant::now() - start_time).as_millis() - uptime_metric.get() as u128)
.try_into()
.unwrap(),
);
let worker_execution_duration = prometheus::register_histogram_vec!(
prometheus::HistogramOpts::new(
@@ -465,6 +461,14 @@ pub async fn run_worker(
)
.expect("register prometheus metric");
let worker_execution_duration_counter = prometheus::register_counter!(prometheus::opts!(
"worker_execution_duration_counter",
"Total number of seconds spent executing jobs"
)
.const_label("name", &worker_name))
.expect("register prometheus metric");
let worker_sleep_duration = prometheus::register_histogram!(prometheus::HistogramOpts::new(
"worker_sleep_duration",
"Duration sleeping waiting for job",
@@ -472,6 +476,15 @@ pub async fn run_worker(
.const_label("name", &worker_name),)
.expect("register prometheus metric");
let worker_sleep_duration_counter = prometheus::register_counter!(prometheus::opts!(
"worker_sleep_duration_counter",
"Total number of seconds spent sleeping between pulling jobs from the queue"
)
.const_label("name", &worker_name))
.expect("register prometheus metric");
let worker_pull_duration = prometheus::register_histogram!(prometheus::HistogramOpts::new(
"worker_pull_duration",
"Duration pulling next job",
@@ -479,6 +492,13 @@ pub async fn run_worker(
.const_label("name", &worker_name),)
.expect("register prometheus metric");
let worker_pull_duration_counter = prometheus::register_counter!(prometheus::opts!(
"worker_pull_duration_counter",
"Total number of seconds spent pulling jobs (if growing large the db is undersized)"
)
.const_label("name", &worker_name))
.expect("register prometheus metric");
let worker_execution_failed = prometheus::register_int_counter_vec!(
prometheus::Opts::new("worker_execution_failed", "Number of failed jobs",)
.const_label("name", &worker_name),
@@ -526,11 +546,12 @@ pub async fn run_worker(
worker_busy.set(0);
uptime_metric.inc_by(
((Instant::now() - start_time).as_millis() - uptime_metric.get() as u128)
(((Instant::now() - start_time).as_millis() as f64)/1000.0 - uptime_metric.get())
.try_into()
.unwrap(),
);
let do_break = async {
if last_ping.elapsed().as_secs() > NUM_SECS_ENV_CHECK {
sqlx::query!(
@@ -574,7 +595,8 @@ pub async fn run_worker(
(job, timer) = {
let timer = worker_pull_duration.start_timer();
pull(&db, WHITELIST_WORKSPACES.clone(), BLACKLIST_WORKSPACES.clone()).map(|x| (x, timer)) } => {
drop(timer);
let duration_pull_s = timer.stop_and_record();
worker_pull_duration_counter.inc_by(duration_pull_s);
(false, job)
},
}
@@ -680,6 +702,8 @@ pub async fn run_worker(
.await;
};
let duration = _timer.stop_and_record();
worker_execution_duration_counter.inc_by(duration);
if !*KEEP_JOB_DIR && !(is_flow && same_worker) {
let _ = tokio::fs::remove_dir_all(job_dir).await;
@@ -689,9 +713,9 @@ pub async fn run_worker(
let _timer = worker_sleep_duration
.start_timer();
tokio::time::sleep(Duration::from_millis(*SLEEP_QUEUE)).await;
let duration = _timer.stop_and_record();
worker_sleep_duration_counter.inc_by(duration);
}
Err(err) => {
tracing::error!(worker = %worker_name, "run_worker: pulling jobs: {}", err);

View File

@@ -1,6 +1,6 @@
// windmill
export { setClient } from "https://deno.land/x/windmill@v1.66.0/mod.ts";
export * from "https://deno.land/x/windmill@v1.66.0/windmill-api/index.ts";
export { setClient } from "https://deno.land/x/windmill@v1.69.3/mod.ts";
export * from "https://deno.land/x/windmill@v1.69.3/windmill-api/index.ts";
// cliffy
export { Command } from "https://deno.land/x/cliffy@v0.25.7/command/command.ts";
@@ -14,7 +14,7 @@ export {
DenoLandProvider,
UpgradeCommand,
} from "https://deno.land/x/cliffy@v0.25.7/command/upgrade/mod.ts";
export { CompletionsCommand } from "https://deno.land/x/cliffy@v0.25.7/command/completions/mod.ts";
// std
export * as path from "https://deno.land/std@0.176.0/path/mod.ts";
export { ensureDir } from "https://deno.land/std@0.176.0/fs/ensure_dir.ts";

View File

@@ -51,9 +51,11 @@ export async function browserLogin(
const url = `${baseUrl}user/cli?port=${port}`
console.log(`Login by going to ${url}`);
try {
open(url)
await open(url)
console.log("Opened browser for you");
} catch { }
} catch {
console.error(`Failed to open browser, please navigate to ${url}`)
}
const firstConnection = await server.accept();
const httpFirstConnection = Deno.serveHttp(firstConnection);
const firstRequest = (await httpFirstConnection.nextRequest())!;

View File

@@ -1,4 +1,4 @@
import { Command, DenoLandProvider, UpgradeCommand } from "./deps.ts";
import { Command, CompletionsCommand, DenoLandProvider, UpgradeCommand } from "./deps.ts";
import flow from "./flow.ts";
import script from "./script.ts";
import workspace from "./workspace.ts";
@@ -13,7 +13,7 @@ import sync from "./sync.ts";
import { tryResolveVersion } from "./context.ts";
import { GlobalOptions } from "./types.ts";
const VERSION = "v1.69.2";
const VERSION = "v1.70.1";
let command: any = new Command()
.name("wmill")
@@ -60,7 +60,8 @@ let command: any = new Command()
],
provider: new DenoLandProvider({ name: "wmill" }),
}),
);
)
.command("completions", new CompletionsCommand());
if (Number.parseInt(VERSION.replace("v", "").replace(".", "")) > 1700) {
command = command

View File

@@ -9,7 +9,7 @@
"lock": "",
"path": null,
"type": "rawscript",
"content": "// import * as wmill from \"https://deno.land/x/windmill@v1.50.0/mod.ts\"\n\nexport async function main(x: string) {\n console.log(\"Hello from Deno! The argument x is \" + x);\n return x;\n}\n",
"content": "// import * as wmill from \"https://deno.land/x/windmill@v1.69.3/mod.ts\"\n\nexport async function main(x: string) {\n console.log(\"Hello from Deno! The argument x is \" + x);\n return x;\n}\n",
"language": "deno",
"input_transforms": {
"x": {

View File

@@ -312,8 +312,9 @@ const command = new Command()
.description("workspace related commands")
.action(list as any)
.command("switch")
.complete("workspace", async () => (await allWorkspaces()).map((x) => x.name))
.description("Switch to another workspace")
.arguments("<workspace_name:string>")
.arguments("<workspace_name:string:workspace>")
.action(switchC as any)
.command("add")
.description("Add a workspace")

View File

@@ -1,12 +1,12 @@
{
"name": "windmill",
"version": "1.69.2",
"version": "1.70.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "windmill",
"version": "1.69.2",
"version": "1.70.1",
"dependencies": {
"@fortawesome/free-brands-svg-icons": "^6.2.1",
"@fortawesome/free-solid-svg-icons": "^6.2.1",
@@ -30,12 +30,12 @@
"svelte-autosize": "^1.0.1",
"svelte-chartjs": "^3.1.0",
"svelte-portal": "^2.2.0",
"svelte-select": "^5.0.2",
"svelte-select": "^5.3.1",
"tailwind-merge": "^1.9.1",
"vscode-ws-jsonrpc": "^2.0.1"
},
"devDependencies": {
"@playwright/test": "^1.29.2",
"@playwright/test": "^1.31.1",
"@sveltejs/adapter-static": "^1.0.0",
"@sveltejs/kit": "^1.0.0-next.589",
"@sveltejs/package": "^1.0.2",
@@ -604,16 +604,16 @@
}
},
"node_modules/@floating-ui/core": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.0.5.tgz",
"integrity": "sha512-iDdOsaCHZH/0FM0yNBYt+cJxJF9S5jrYWNtDZOiDFMiZ7uxMJ/71h8eTwoVifEAruv9p9rlMPYCGIgMjOz95FQ=="
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.2.1.tgz",
"integrity": "sha512-LSqwPZkK3rYfD7GKoIeExXOyYx6Q1O4iqZWwIehDNuv3Dv425FIAE8PRwtAx1imEolFTHgBEcoFHm9MDnYgPCg=="
},
"node_modules/@floating-ui/dom": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.1.0.tgz",
"integrity": "sha512-TSogMPVxbRe77QCj1dt8NmRiJasPvuc+eT5jnJ6YpLqgOD2zXc5UA3S1qwybN+GVCDNdKfpKy1oj8RpzLJvh6A==",
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.2.1.tgz",
"integrity": "sha512-Rt45SmRiV8eU+xXSB9t0uMYiQ/ZWGE/jumse2o3i5RGlyvcbqOF4q+1qBnzLE2kZ5JGhq0iMkcGXUKbFe7MpTA==",
"dependencies": {
"@floating-ui/core": "^1.0.5"
"@floating-ui/core": "^1.2.1"
}
},
"node_modules/@fortawesome/fontawesome-common-types": {
@@ -754,19 +754,22 @@
}
},
"node_modules/@playwright/test": {
"version": "1.29.2",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.29.2.tgz",
"integrity": "sha512-+3/GPwOgcoF0xLz/opTnahel1/y42PdcgZ4hs+BZGIUjtmEFSXGg+nFoaH3NSmuc7a6GSFwXDJ5L7VXpqzigNg==",
"version": "1.31.1",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.31.1.tgz",
"integrity": "sha512-IsytVZ+0QLDh1Hj83XatGp/GsI1CDJWbyDaBGbainsh0p2zC7F4toUocqowmjS6sQff2NGT3D9WbDj/3K2CJiA==",
"dev": true,
"dependencies": {
"@types/node": "*",
"playwright-core": "1.29.2"
"playwright-core": "1.31.1"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=14"
},
"optionalDependencies": {
"fsevents": "2.3.2"
}
},
"node_modules/@polka/url": {
@@ -2458,7 +2461,8 @@
"node_modules/dedent-js": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dedent-js/-/dedent-js-1.0.1.tgz",
"integrity": "sha512-OUepMozQULMLUmhxS95Vudo0jb0UchLimi3+pQ2plj61Fcy8axbP9hbiD4Sz6DPqn6XG3kfmziVfQ1rSys5AJQ=="
"integrity": "sha512-OUepMozQULMLUmhxS95Vudo0jb0UchLimi3+pQ2plj61Fcy8axbP9hbiD4Sz6DPqn6XG3kfmziVfQ1rSys5AJQ==",
"dev": true
},
"node_modules/deep-extend": {
"version": "0.6.0",
@@ -3873,6 +3877,7 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
"integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
"dev": true,
"dependencies": {
"tslib": "^2.0.3"
}
@@ -4268,6 +4273,7 @@
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
"integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
"dev": true,
"dependencies": {
"lower-case": "^2.0.2",
"tslib": "^2.0.3"
@@ -4502,6 +4508,7 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz",
"integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==",
"dev": true,
"dependencies": {
"no-case": "^3.0.4",
"tslib": "^2.0.3"
@@ -4596,9 +4603,9 @@
}
},
"node_modules/playwright-core": {
"version": "1.29.2",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.29.2.tgz",
"integrity": "sha512-94QXm4PMgFoHAhlCuoWyaBYKb92yOcGVHdQLoxQ7Wjlc7Flg4aC/jbFW7xMR52OfXMVkWicue4WXE7QEegbIRA==",
"version": "1.31.1",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.31.1.tgz",
"integrity": "sha512-JTyX4kV3/LXsvpHkLzL2I36aCdml4zeE35x+G5aPc4bkLsiRiQshU5lWeVpHFAuC8xAcbI6FDcw/8z3q2xtJSQ==",
"dev": true,
"bin": {
"playwright": "cli.js"
@@ -6206,25 +6213,12 @@
}
},
"node_modules/svelte-floating-ui": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/svelte-floating-ui/-/svelte-floating-ui-1.1.6.tgz",
"integrity": "sha512-lJfrc34+xSxKs+5AtnYj32uRIjaxGjhT92cOiNL3zEqaJ755JxUqXCf6tk+9G1wkCuqkK9A8tscVBsyY3iLmVQ==",
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/svelte-floating-ui/-/svelte-floating-ui-1.2.8.tgz",
"integrity": "sha512-8Ifi5CD2Ui7FX7NjJRmutFtXjrB8T/FMNoS2H8P81t5LHK4I9G4NIs007rLWG/nRl7y+zJUXa3tWuTjYXw/O5A==",
"dependencies": {
"@floating-ui/dom": "^1.0.4",
"svelte2tsx": "^0.5.11"
}
},
"node_modules/svelte-floating-ui/node_modules/svelte2tsx": {
"version": "0.5.23",
"resolved": "https://registry.npmjs.org/svelte2tsx/-/svelte2tsx-0.5.23.tgz",
"integrity": "sha512-jYFnugTQRFmUpvLXPQrKzVYcW5ErT+0QCxg027Zx9BuvYefMZFuoBSTDYe7viPEFGrPPiLgT2m7f5n9khE7f7Q==",
"dependencies": {
"dedent-js": "^1.0.1",
"pascal-case": "^3.1.1"
},
"peerDependencies": {
"svelte": "^3.24",
"typescript": "^4.1.2"
"@floating-ui/core": "^1.1.0",
"@floating-ui/dom": "^1.1.0"
}
},
"node_modules/svelte-highlight": {
@@ -6348,12 +6342,12 @@
"dev": true
},
"node_modules/svelte-select": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/svelte-select/-/svelte-select-5.0.2.tgz",
"integrity": "sha512-SsyIwcRVl38041xk7HoPCAew8D6ypTrMvoNcwOgAEWz9Yycgd/uZ58gMSubxSDtkW/ohuYmmHf5OlGCKIqn9Cw==",
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/svelte-select/-/svelte-select-5.3.1.tgz",
"integrity": "sha512-TKlH22FEOr/KYNX4dzBJntNzeI0v0eZYU3VEo2uz/gar6xZdhQ3KT81hvD595GfSB7wiFdYc9SO42wrcgm9W4Q==",
"dependencies": {
"@floating-ui/dom": "^1.0.10",
"svelte-floating-ui": "1.1.6"
"@floating-ui/dom": "^1.2.1",
"svelte-floating-ui": "1.2.8"
}
},
"node_modules/svelte-splitpanes": {
@@ -6691,6 +6685,7 @@
"version": "4.9.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz",
"integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -7486,16 +7481,16 @@
}
},
"@floating-ui/core": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.0.5.tgz",
"integrity": "sha512-iDdOsaCHZH/0FM0yNBYt+cJxJF9S5jrYWNtDZOiDFMiZ7uxMJ/71h8eTwoVifEAruv9p9rlMPYCGIgMjOz95FQ=="
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.2.1.tgz",
"integrity": "sha512-LSqwPZkK3rYfD7GKoIeExXOyYx6Q1O4iqZWwIehDNuv3Dv425FIAE8PRwtAx1imEolFTHgBEcoFHm9MDnYgPCg=="
},
"@floating-ui/dom": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.1.0.tgz",
"integrity": "sha512-TSogMPVxbRe77QCj1dt8NmRiJasPvuc+eT5jnJ6YpLqgOD2zXc5UA3S1qwybN+GVCDNdKfpKy1oj8RpzLJvh6A==",
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.2.1.tgz",
"integrity": "sha512-Rt45SmRiV8eU+xXSB9t0uMYiQ/ZWGE/jumse2o3i5RGlyvcbqOF4q+1qBnzLE2kZ5JGhq0iMkcGXUKbFe7MpTA==",
"requires": {
"@floating-ui/core": "^1.0.5"
"@floating-ui/core": "^1.2.1"
}
},
"@fortawesome/fontawesome-common-types": {
@@ -7602,13 +7597,14 @@
}
},
"@playwright/test": {
"version": "1.29.2",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.29.2.tgz",
"integrity": "sha512-+3/GPwOgcoF0xLz/opTnahel1/y42PdcgZ4hs+BZGIUjtmEFSXGg+nFoaH3NSmuc7a6GSFwXDJ5L7VXpqzigNg==",
"version": "1.31.1",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.31.1.tgz",
"integrity": "sha512-IsytVZ+0QLDh1Hj83XatGp/GsI1CDJWbyDaBGbainsh0p2zC7F4toUocqowmjS6sQff2NGT3D9WbDj/3K2CJiA==",
"dev": true,
"requires": {
"@types/node": "*",
"playwright-core": "1.29.2"
"fsevents": "2.3.2",
"playwright-core": "1.31.1"
}
},
"@polka/url": {
@@ -8781,7 +8777,8 @@
"dedent-js": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dedent-js/-/dedent-js-1.0.1.tgz",
"integrity": "sha512-OUepMozQULMLUmhxS95Vudo0jb0UchLimi3+pQ2plj61Fcy8axbP9hbiD4Sz6DPqn6XG3kfmziVfQ1rSys5AJQ=="
"integrity": "sha512-OUepMozQULMLUmhxS95Vudo0jb0UchLimi3+pQ2plj61Fcy8axbP9hbiD4Sz6DPqn6XG3kfmziVfQ1rSys5AJQ==",
"dev": true
},
"deep-extend": {
"version": "0.6.0",
@@ -9869,6 +9866,7 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
"integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
"dev": true,
"requires": {
"tslib": "^2.0.3"
}
@@ -10173,6 +10171,7 @@
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
"integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
"dev": true,
"requires": {
"lower-case": "^2.0.2",
"tslib": "^2.0.3"
@@ -10344,6 +10343,7 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz",
"integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==",
"dev": true,
"requires": {
"no-case": "^3.0.4",
"tslib": "^2.0.3"
@@ -10417,9 +10417,9 @@
"dev": true
},
"playwright-core": {
"version": "1.29.2",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.29.2.tgz",
"integrity": "sha512-94QXm4PMgFoHAhlCuoWyaBYKb92yOcGVHdQLoxQ7Wjlc7Flg4aC/jbFW7xMR52OfXMVkWicue4WXE7QEegbIRA==",
"version": "1.31.1",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.31.1.tgz",
"integrity": "sha512-JTyX4kV3/LXsvpHkLzL2I36aCdml4zeE35x+G5aPc4bkLsiRiQshU5lWeVpHFAuC8xAcbI6FDcw/8z3q2xtJSQ==",
"dev": true
},
"postcss": {
@@ -11539,23 +11539,12 @@
}
},
"svelte-floating-ui": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/svelte-floating-ui/-/svelte-floating-ui-1.1.6.tgz",
"integrity": "sha512-lJfrc34+xSxKs+5AtnYj32uRIjaxGjhT92cOiNL3zEqaJ755JxUqXCf6tk+9G1wkCuqkK9A8tscVBsyY3iLmVQ==",
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/svelte-floating-ui/-/svelte-floating-ui-1.2.8.tgz",
"integrity": "sha512-8Ifi5CD2Ui7FX7NjJRmutFtXjrB8T/FMNoS2H8P81t5LHK4I9G4NIs007rLWG/nRl7y+zJUXa3tWuTjYXw/O5A==",
"requires": {
"@floating-ui/dom": "^1.0.4",
"svelte2tsx": "^0.5.11"
},
"dependencies": {
"svelte2tsx": {
"version": "0.5.23",
"resolved": "https://registry.npmjs.org/svelte2tsx/-/svelte2tsx-0.5.23.tgz",
"integrity": "sha512-jYFnugTQRFmUpvLXPQrKzVYcW5ErT+0QCxg027Zx9BuvYefMZFuoBSTDYe7viPEFGrPPiLgT2m7f5n9khE7f7Q==",
"requires": {
"dedent-js": "^1.0.1",
"pascal-case": "^3.1.1"
}
}
"@floating-ui/core": "^1.1.0",
"@floating-ui/dom": "^1.1.0"
}
},
"svelte-highlight": {
@@ -11621,12 +11610,12 @@
"dev": true
},
"svelte-select": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/svelte-select/-/svelte-select-5.0.2.tgz",
"integrity": "sha512-SsyIwcRVl38041xk7HoPCAew8D6ypTrMvoNcwOgAEWz9Yycgd/uZ58gMSubxSDtkW/ohuYmmHf5OlGCKIqn9Cw==",
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/svelte-select/-/svelte-select-5.3.1.tgz",
"integrity": "sha512-TKlH22FEOr/KYNX4dzBJntNzeI0v0eZYU3VEo2uz/gar6xZdhQ3KT81hvD595GfSB7wiFdYc9SO42wrcgm9W4Q==",
"requires": {
"@floating-ui/dom": "^1.0.10",
"svelte-floating-ui": "1.1.6"
"@floating-ui/dom": "^1.2.1",
"svelte-floating-ui": "1.2.8"
}
},
"svelte-splitpanes": {
@@ -11877,7 +11866,8 @@
"typescript": {
"version": "4.9.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz",
"integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg=="
"integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==",
"dev": true
},
"uglify-js": {
"version": "3.16.3",

View File

@@ -1,6 +1,6 @@
{
"name": "windmill",
"version": "1.69.2",
"version": "1.70.1",
"scripts": {
"dev": "vite dev",
"build": "vite build",
@@ -15,7 +15,7 @@
"test": "playwright test --config=tests-out/playwright.config.js"
},
"devDependencies": {
"@playwright/test": "^1.29.2",
"@playwright/test": "^1.31.1",
"@sveltejs/adapter-static": "^1.0.0",
"@sveltejs/kit": "^1.0.0-next.589",
"@sveltejs/package": "^1.0.2",
@@ -82,7 +82,7 @@
"svelte-autosize": "^1.0.1",
"svelte-chartjs": "^3.1.0",
"svelte-portal": "^2.2.0",
"svelte-select": "^5.0.2",
"svelte-select": "^5.3.1",
"tailwind-merge": "^1.9.1",
"vscode-ws-jsonrpc": "^2.0.1"
},

View File

@@ -2,9 +2,10 @@
import { Highlight } from 'svelte-highlight'
import { json } from 'svelte-highlight/languages'
import TableCustom from './TableCustom.svelte'
import { truncate } from '$lib/utils'
import { Button } from './common'
import { copyToClipboard, truncate } from '$lib/utils'
import { Button, Drawer, DrawerContent } from './common'
import autosize from 'svelte-autosize'
import { ClipboardCopy } from 'lucide-svelte'
export let result: any
export let requireHtmlApproval = false
@@ -86,16 +87,36 @@
return 'json'
}
let payload = ''
let jsonViewer: Drawer
</script>
<Drawer bind:this={jsonViewer} size="900px">
<DrawerContent title="Expanded Result" on:close={jsonViewer.closeDrawer}>
<svelte:fragment slot="actions">
<Button
on:click={() => copyToClipboard(JSON.stringify(result, null, 4))}
color="light"
size="xs"
>
<div class="flex gap-2 items-center">Copy to clipboard <ClipboardCopy /> </div>
</Button>
</svelte:fragment>
<Highlight language={json} code={JSON.stringify(result, null, 4).replace(/\\n/g, '\n')} />
</DrawerContent>
</Drawer>
<div class="inline-highlight">
{#if result != undefined}
{#if resultKind && resultKind != 'json'}
<div class="mb-2 text-gray-500 text-sm bg-gray-50/20">
as JSON&nbsp;<input type="checkbox" bind:checked={forceJson} /></div
>{/if}{#if typeof result == 'object' && Object.keys(result).length > 0}<div
class="mb-2 text-sm text-gray-700"
>The result keys are: <b>{truncate(Object.keys(result).join(', '), 50)}</b></div
class="mb-2 text-sm text-gray-700 relative"
>The result keys are: <b>{truncate(Object.keys(result).join(', '), 50)}</b>
<div class="text-gray-500 text-sm absolute top-0 right-2">
<button on:click={jsonViewer.openDrawer}>Expand JSON</button>
</div></div
>{/if}{#if !forceJson && resultKind == 'table-col'}<div
class="grid grid-flow-col-dense border border-gray-200 rounded-md "
>
@@ -223,10 +244,8 @@
><a rel="noreferrer" target="_blank" href={result['approvalPage']}>Approval Page</a></div
>
</div>
{:else}<Highlight
language={json}
code={JSON.stringify(result, null, 4).replace(/\\n/g, '\n')}
/>
{:else}
<Highlight language={json} code={JSON.stringify(result, null, 4).replace(/\\n/g, '\n')} />
{/if}
{:else}
<div class="text-gray-500 text-sm">No result</div>

View File

@@ -13,6 +13,7 @@
import UnsavedConfirmationModal from './common/confirmationModal/UnsavedConfirmationModal.svelte'
import { OFFSET } from './CronInput.svelte'
import FlowGraphViewer from './FlowGraphViewer.svelte'
import ScriptEditorDrawer from './flows/content/ScriptEditorDrawer.svelte'
import FlowEditor from './flows/FlowEditor.svelte'
import { flowStateStore } from './flows/flowState'
import { flowStore } from './flows/flowStore'
@@ -145,6 +146,7 @@
const scheduleStore = writable<Schedule>({ args: {}, cron: '', enabled: false })
const previewArgsStore = writable<Record<string, any>>(initialArgs)
const scriptEditorDrawer = writable<ScriptEditorDrawer | undefined>(undefined)
function select(selectedId: string) {
selectedIdStore.set(selectedId)
@@ -154,7 +156,8 @@
selectedId: selectedIdStore,
schedule: scheduleStore,
select,
previewArgs: previewArgsStore
previewArgs: previewArgsStore,
scriptEditorDrawer
})
async function loadSchedule() {
@@ -181,6 +184,7 @@
</script>
{#if !$userStore?.operator}
<ScriptEditorDrawer bind:this={$scriptEditorDrawer} />
<UnsavedConfirmationModal />
<Drawer bind:this={flowViewer} size="75%">

View File

@@ -149,7 +149,7 @@
</div>
<FlowProgressBar {job} bind:reset={jobProgressReset} />
<div class="overflow-y-auto grow divide-y divide-gray-600 ">
<div class="overflow-y-auto grow divide-y divide-gray-600 pr-4">
<div class="max-h-1/2 overflow-auto border-b border-gray-700">
<SchemaForm
noVariablePicker

View File

@@ -96,7 +96,7 @@
localFlowModuleStates?.[innerModules?.[i - 1]?.id ?? '']?.type ==
FlowStatusModule.type.SUCCESS
) {
localFlowModuleStates[mod.id ?? ''] = { type: mod.type }
localFlowModuleStates[mod.id ?? ''] = { type: mod.type, args: job?.args }
} else if (
mod.type === FlowStatusModule.type.WAITING_FOR_EXECUTOR &&
localFlowModuleStates[mod.id ?? '']?.scheduled_for == undefined
@@ -109,7 +109,8 @@
type: mod.type,
scheduled_for: 'scheduled for ' + displayDate(job?.['scheduled_for'], true),
job_id: job?.id,
parent_module: mod['parent_module']
parent_module: mod['parent_module'],
args: job?.args
}
})
}
@@ -329,10 +330,12 @@
type: FlowStatusModule.type.IN_PROGRESS,
logs: e.detail.logs,
job_id: e.detail.id,
args: e.detail.args,
iteration_total: flowJobIds?.flowJobs.length
}
} else {
localFlowModuleStates[flowJobIds.moduleId] = {
args: e.detail.args,
type: e.detail.success
? FlowStatusModule.type.SUCCESS
: FlowStatusModule.type.FAILURE,
@@ -397,10 +400,12 @@
localFlowModuleStates[mod.id] = {
type: FlowStatusModule.type.IN_PROGRESS,
logs: e.detail.logs,
args: e.detail.args,
parent_module: mod['parent_module']
}
} else {
localFlowModuleStates[mod.id] = {
args: e.detail.args,
type: e.detail.success
? FlowStatusModule.type.SUCCESS
: FlowStatusModule.type.FAILURE,
@@ -497,6 +502,9 @@
</div>
{/if}
</div>
<div class="px-1 border-b border-black">
<JobArgs args={node.args} />
</div>
<FlowJobResult
loading={job['running'] == true}
noBorder

View File

@@ -8,6 +8,7 @@
import Select from 'svelte-select'
import AppConnect from './AppConnect.svelte'
import ResourceEditor from './ResourceEditor.svelte'
import { SELECT_INPUT_DEFAULT_STYLE } from '../defaults'
const dispatch = createEventDispatcher()
let resources: Resource[] = []
@@ -59,12 +60,13 @@
<div class="flex flex-row gap-x-1 w-full">
<Select
listAutoWidth={false}
--height="34px"
value={collection.find((x) => x.value == value)}
bind:justValue={value}
items={collection}
class="text-clip grow min-w-0"
placeholder="{resourceType} resource"
inputStyles={SELECT_INPUT_DEFAULT_STYLE.inputStyles}
containerStyles={SELECT_INPUT_DEFAULT_STYLE.containerStyles}
/>
{#if value && value != ''}
<Button variant="border" size="xs" on:click={() => resourceEditor?.initEdit?.(value ?? '')}>

View File

@@ -11,6 +11,7 @@
import { Button, Drawer, DrawerContent } from './common'
import HighlightCode from './HighlightCode.svelte'
import FlowPathViewer from './flows/content/FlowPathViewer.svelte'
import { SELECT_INPUT_DEFAULT_STYLE } from '../defaults'
export let initialPath: string | undefined = undefined
export let scriptPath: string | undefined = undefined
@@ -87,6 +88,8 @@
bind:justValue={scriptPath}
{items}
placeholder="Pick a {itemKind}"
inputStyles={SELECT_INPUT_DEFAULT_STYLE.inputStyles}
containerStyles={SELECT_INPUT_DEFAULT_STYLE.containerStyles}
/>
{/if}

View File

@@ -110,6 +110,10 @@
}
}
export function focus() {
editor?.focus()
}
let width = 0
async function loadMonaco() {
model = meditor.createModel(code, lang, mUri.parse(uri))

View File

@@ -1,5 +1,6 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte'
import { twMerge } from 'tailwind-merge'
export let options: {
left?: string
@@ -7,6 +8,8 @@
} = {}
export let checked: boolean = false
export let disabled = false
export let textClass = ''
export let textStyle = ''
export let size: 'sm' | 'xs' = 'sm'
const id = (Math.random() + 1).toString(36).substring(10)
@@ -22,7 +25,13 @@
>
{#if Boolean(options?.left)}
<span
class="mr-2 text-sm font-medium duration-200 {disabled ? 'text-gray-600' : 'text-gray-900'}"
class={twMerge(
'ml-2 font-medium duration-200',
disabled ? 'text-gray-500' : 'text-gray-900',
size === 'xs' ? 'text-xs' : 'text-sm',
textClass
)}
style={textStyle}
>
{options?.left}
</span>
@@ -51,9 +60,13 @@
</div>
{#if Boolean(options?.right)}
<span
class="ml-2 text-sm font-medium duration-200
{disabled ? 'text-gray-500' : 'text-gray-900'}
{size === 'xs' ? 'text-xs' : 'text-sm'}"
class={twMerge(
'ml-2 font-medium duration-200',
disabled ? 'text-gray-500' : 'text-gray-900',
size === 'xs' ? 'text-xs' : 'text-sm',
textClass
)}
style={textStyle}
>
{options?.right}
</span>

View File

@@ -5,7 +5,8 @@
import { Icon } from 'svelte-awesome'
import type { AppInput } from '../../inputType'
import type { Output } from '../../rx'
import type { AppEditorContext } from '../../types'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import { concatCustomCss } from '../../utils'
import AlignWrapper from '../helpers/AlignWrapper.svelte'
import InputValue from '../helpers/InputValue.svelte'
import type RunnableComponent from '../helpers/RunnableComponent.svelte'
@@ -17,10 +18,11 @@
export let recomputeIds: string[] | undefined = undefined
export let extraQueryParams: Record<string, any> = {}
export let horizontalAlignment: 'left' | 'center' | 'right' | undefined = undefined
export let customCss: ComponentCustomCSS<'container' | 'button'> | undefined = undefined
export const staticOutputs: string[] = ['loading', 'result']
const { runnableComponents, worldStore } = getContext<AppEditorContext>('AppEditorContext')
const { app, runnableComponents, worldStore } = getContext<AppEditorContext>('AppEditorContext')
let labelValue: string = 'Default label'
let color: ButtonType.Color
@@ -47,6 +49,8 @@
isLoading = value
}
})
$: css = concatCustomCss($app.css?.formcomponent, customCss)
</script>
<InputValue {id} input={configuration.goto} bind:value={goto} />
@@ -64,9 +68,13 @@
autoRefresh={false}
forceSchemaDisplay={true}
runnableClass="!block"
runnableStyle={css?.container.style}
>
<AlignWrapper {horizontalAlignment}>
<div class="flex flex-col gap-2 px-4 w-full">
<div
class="flex flex-col gap-2 px-4 w-full {css?.container?.class ?? ''}"
style={css?.container?.style ?? ''}
>
<div>
{#if noInputs}
<div class="text-gray-600 italic text-sm my-4">
@@ -84,7 +92,8 @@
{#if !noInputs}
<Button
loading={isLoading}
btnClasses="my-1"
btnClasses="my-1 {css?.button?.class ?? ''}"
style={css?.button?.style ?? ''}
on:pointerdown={(e) => {
e?.stopPropagation()
window.dispatchEvent(new Event('pointerup'))

View File

@@ -5,13 +5,14 @@
import { Icon } from 'svelte-awesome'
import type { AppInput } from '../../inputType'
import type { Output } from '../../rx'
import type { AppEditorContext } from '../../types'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import AlignWrapper from '../helpers/AlignWrapper.svelte'
import InputValue from '../helpers/InputValue.svelte'
import type RunnableComponent from '../helpers/RunnableComponent.svelte'
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
import Portal from 'svelte-portal'
import Modal from '$lib/components/common/modal/Modal.svelte'
import { concatCustomCss } from '../../utils'
export let id: string
export let componentInput: AppInput | undefined
@@ -20,10 +21,11 @@
export let extraQueryParams: Record<string, any> = {}
export let horizontalAlignment: 'left' | 'center' | 'right' | undefined = undefined
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
export let customCss: ComponentCustomCSS<'button' | 'popup'> | undefined = undefined
export const staticOutputs: string[] = ['loading', 'result']
const { runnableComponents, worldStore } = getContext<AppEditorContext>('AppEditorContext')
const { app, runnableComponents, worldStore } = getContext<AppEditorContext>('AppEditorContext')
let labelValue: string = 'Default label'
let color: ButtonType.Color
@@ -35,6 +37,8 @@
let ownClick: boolean = false
let errors: Record<string, string> = {}
let open: boolean = false
$: errorsMessage = Object.values(errors)
.filter((x) => x != '')
.join('\n')
@@ -62,7 +66,7 @@
$: loading = isLoading && ownClick
let open: boolean = false
$: css = concatCustomCss($app?.css?.formbuttoncomponent, customCss)
</script>
<InputValue {id} input={configuration.label} bind:value={labelValue} />
@@ -79,6 +83,8 @@
<Modal
{open}
title={labelValue}
class={css?.popup.class}
style={css?.popup.style}
on:canceled={() => {
open = false
}}
@@ -148,6 +154,8 @@
{disabled}
{size}
{color}
btnClasses={css?.button?.class ?? ''}
style={css?.button?.style ?? ''}
on:click={(e) => {
open = true
}}

View File

@@ -15,13 +15,18 @@
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
import type { AppInput } from '../../inputType'
import InputValue from '../helpers/InputValue.svelte'
import { concatCustomCss } from '../../utils'
import { getContext } from 'svelte'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
export let id: string
export let componentInput: AppInput | undefined
export let configuration: Record<string, AppInput>
export let initializing: boolean | undefined = undefined
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
export const staticOutputs: string[] = ['loading', 'result']
const { app } = getContext<AppEditorContext>('AppEditorContext')
ChartJS.register(
Title,
@@ -77,17 +82,21 @@
}
]
}
$: css = concatCustomCss($app.css?.barchartcomponent, customCss)
</script>
<InputValue {id} input={configuration.theme} bind:value={theme} />
<InputValue {id} input={configuration.line} bind:value={lineChart} />
<RunnableWrapper flexWrap autoRefresh bind:componentInput {id} bind:initializing bind:result>
{#if result}
{#if lineChart}
<Line {data} options={lineOptions} />
{:else}
<Bar {data} options={barOptions} />
<div class="w-full h-full {css?.container?.class ?? ''}" style={css?.container?.style ?? ''}>
{#if result}
{#if lineChart}
<Line {data} options={lineOptions} />
{:else}
<Bar {data} options={barOptions} />
{/if}
{/if}
{/if}
</div>
</RunnableWrapper>

View File

@@ -1,16 +1,23 @@
<script lang="ts">
import { getContext } from 'svelte'
import type { AppInput } from '../../inputType'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import { concatCustomCss } from '../../utils'
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
export let id: string
export let componentInput: AppInput | undefined
export let initializing: boolean | undefined = undefined
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
export const staticOutputs: string[] = ['result', 'loading']
const { app } = getContext<AppEditorContext>('AppEditorContext')
let result: string | undefined = undefined
let h: number | undefined = undefined
let w: number | undefined = undefined
$: css = concatCustomCss($app.css?.htmlcomponent, customCss)
</script>
<div
@@ -25,8 +32,8 @@
{#key result}
<iframe
frameborder="0"
style="height: {h}px; width: {w}px"
class="p-0"
style="height: {h}px; width: {w}px; {css?.container?.style ?? ''}"
class="p-0 {css?.container?.class ?? ''}"
title="sandbox"
srcdoc={result
? '<scr' + `ipt type="application/javascript" src="/tailwind.js"></script>` + result

View File

@@ -14,13 +14,18 @@
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
import type { AppInput } from '../../inputType'
import InputValue from '../helpers/InputValue.svelte'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import { concatCustomCss } from '../../utils'
import { getContext } from 'svelte'
export let id: string
export let componentInput: AppInput | undefined
export let configuration: Record<string, AppInput>
export let initializing: boolean | undefined = undefined
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
export const staticOutputs: string[] = ['loading', 'result']
const { app } = getContext<AppEditorContext>('AppEditorContext')
ChartJS.register(
Title,
@@ -60,17 +65,21 @@
}
]
}
$: css = concatCustomCss($app.css?.piechartcomponent, customCss)
</script>
<InputValue {id} input={configuration.theme} bind:value={theme} />
<InputValue {id} input={configuration.doughnutStyle} bind:value={doughnut} />
<RunnableWrapper flexWrap autoRefresh bind:componentInput {id} bind:initializing bind:result>
{#if result}
{#if doughnut}
<Doughnut {data} {options} />
{:else}
<Pie {data} {options} />
<div class="w-full h-full {css?.container?.class ?? ''}" style={css?.container?.style ?? ''}>
{#if result}
{#if doughnut}
<Doughnut {data} {options} />
{:else}
<Pie {data} {options} />
{/if}
{/if}
{/if}
</div>
</RunnableWrapper>

View File

@@ -17,16 +17,21 @@
import Scatter from 'svelte-chartjs/Scatter.svelte'
import InputValue from '../helpers/InputValue.svelte'
import type { ChartOptions, ChartData } from 'chart.js'
import { concatCustomCss } from '../../utils'
import { getContext } from 'svelte'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
export let id: string
export let componentInput: AppInput | undefined
export let configuration: Record<string, AppInput>
export let initializing: boolean | undefined = undefined
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
let zoomable = false
let pannable = false
export const staticOutputs: string[] = ['loading', 'result']
const { app } = getContext<AppEditorContext>('AppEditorContext')
ChartJS.register(
Title,
@@ -66,13 +71,17 @@
$: data = {
datasets: result ?? []
} as ChartData<'scatter', (number | Point)[], unknown>
$: css = concatCustomCss($app.css?.scatterchartcomponent, customCss)
</script>
<InputValue {id} input={configuration.zoomable} bind:value={zoomable} />
<InputValue {id} input={configuration.pannable} bind:value={pannable} />
<RunnableWrapper flexWrap autoRefresh bind:componentInput {id} bind:initializing bind:result>
{#if result}
<Scatter {data} {options} />
{/if}
<div class="w-full h-full {css?.container?.class ?? ''}" style={css?.container?.style ?? ''}>
{#if result}
<Scatter {data} {options} />
{/if}
</div>
</RunnableWrapper>

View File

@@ -20,13 +20,18 @@
import { Scatter } from 'svelte-chartjs'
import InputValue from '../helpers/InputValue.svelte'
import type { ChartOptions, ChartData } from 'chart.js'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import { getContext } from 'svelte'
import { concatCustomCss } from '../../utils'
export let id: string
export let componentInput: AppInput | undefined
export let configuration: Record<string, AppInput>
export let initializing: boolean | undefined = undefined
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
export const staticOutputs: string[] = ['loading', 'result']
const { app } = getContext<AppEditorContext>('AppEditorContext')
let logarithmicScale = false
let zoomable = false
@@ -80,6 +85,8 @@
$: data = {
datasets: result ?? []
} as ChartData<'scatter', (number | Point)[], unknown>
$: css = concatCustomCss($app.css?.timeseriescomponent, customCss)
</script>
<InputValue {id} input={configuration.logarithmicScale} bind:value={logarithmicScale} />
@@ -87,7 +94,9 @@
<InputValue {id} input={configuration.pannable} bind:value={pannable} />
<RunnableWrapper flexWrap autoRefresh bind:componentInput {id} bind:initializing bind:result>
{#if result}
<Scatter {data} {options} />
{/if}
<div class="w-full h-full {css?.container?.class ?? ''}" style={css?.container?.style ?? ''}>
{#if result}
<Scatter {data} {options} />
{/if}
</div>
</RunnableWrapper>

View File

@@ -1,12 +1,11 @@
<script lang="ts">
import { Loader2 } from 'lucide-svelte'
import { onMount } from 'svelte'
import type { AppInput } from '../../inputType'
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
export let id: string
export let componentInput: AppInput | undefined
export let configuration: Record<string, AppInput>
// export let configuration: Record<string, AppInput>
export let initializing: boolean | undefined = undefined
export const staticOutputs: string[] = ['result', 'loading']
@@ -42,11 +41,6 @@
<div class="w-full h-full" bind:clientHeight={h} bind:clientWidth={w}>
<RunnableWrapper flexWrap bind:componentInput {id} bind:initializing bind:result>
{#if !Plotly}
<div class="p-2">
<Loader2 class="animate-spin" />
</div>
{/if}
<div on:pointerdown bind:this={divEl} />
</RunnableWrapper>
</div>

View File

@@ -1,5 +1,4 @@
<script lang="ts">
import { Loader2 } from 'lucide-svelte'
import { onMount } from 'svelte'
import type { AppInput } from '../../inputType'
import InputValue from '../helpers/InputValue.svelte'
@@ -54,11 +53,6 @@
<div class="w-full h-full" bind:clientHeight={h} bind:clientWidth={w}>
<RunnableWrapper flexWrap bind:componentInput {id} bind:initializing bind:result>
{#if !vegaEmbed}
<div class="p-2">
<Loader2 class="animate-spin" />
</div>
{/if}
<div on:pointerdown bind:this={divEl} />
</RunnableWrapper>
</div>

View File

@@ -65,6 +65,8 @@
let columnDefs: any = undefined
let allEditable: boolean | undefined = undefined
let pagination: boolean | undefined = undefined
let pageSize: number | undefined = 10
function onCellValueChanged(event) {
if (result) {
@@ -79,6 +81,8 @@
<InputValue {id} input={configuration.columnDefs} bind:value={columnDefs} />
<InputValue {id} input={configuration.allEditable} bind:value={allEditable} />
<InputValue {id} input={configuration.pagination} bind:value={pagination} />
<InputValue {id} input={configuration.pageSize} bind:value={pageSize} />
<RunnableWrapper flexWrap bind:componentInput {id} bind:initializing bind:result>
{#if Array.isArray(result) && result.every(isObject)}
@@ -95,11 +99,15 @@
style:width="{clientWidth}px"
class="ag-theme-alpine"
>
<AgGridSvelte
bind:rowData={result}
{columnDefs}
defaultColDef={{ flex: 1, editable: allEditable, onCellValueChanged }}
/>
{#key pagination}
<AgGridSvelte
bind:rowData={result}
{columnDefs}
{pagination}
paginationPageSize={pageSize}
defaultColDef={{ flex: 1, editable: allEditable, onCellValueChanged }}
/>
{/key}
</div>
</div>
{:else if result != undefined}

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import { getContext, onMount } from 'svelte'
import type { Output } from '../../../rx'
import type { AppEditorContext, BaseAppComponent } from '../../../types'
import type { AppEditorContext, BaseAppComponent, ComponentCustomCSS } from '../../../types'
import InputValue from '../../helpers/InputValue.svelte'
import type { AppInput } from '../../../inputType'
import RunnableWrapper from '../../helpers/RunnableWrapper.svelte'
@@ -14,12 +14,17 @@
import { tableOptions } from './tableOptions'
import Alert from '$lib/components/common/alert/Alert.svelte'
import type { ButtonComponent } from '../../../editor/component'
import { concatCustomCss } from '../../../utils'
import { twMerge } from 'tailwind-merge'
export let id: string
export let componentInput: AppInput | undefined
export let configuration: Record<string, AppInput>
export let actionButtons: (BaseAppComponent & ButtonComponent)[]
export let initializing: boolean | undefined = undefined
export let customCss:
| ComponentCustomCSS<'container' | 'tableHeader' | 'tableBody' | 'tableFooter'>
| undefined = undefined
export const staticOutputs: string[] = [
'selectedRow',
@@ -51,8 +56,11 @@
let table = createSvelteTable(options)
const { worldStore, staticOutputs: staticOutputsStore } =
getContext<AppEditorContext>('AppEditorContext')
const {
app,
worldStore,
staticOutputs: staticOutputsStore
} = getContext<AppEditorContext>('AppEditorContext')
let selectedRowIndex = -1
@@ -133,13 +141,22 @@
}
$: result && rerender()
$: css = concatCustomCss($app.css?.tablecomponent, customCss)
</script>
<InputValue {id} input={configuration.search} bind:value={search} />
<RunnableWrapper flexWrap bind:componentInput {id} bind:initializing bind:result>
{#if Array.isArray(result) && result.every(isObject)}
<div class="border border-gray-300 shadow-sm divide-y divide-gray-300 flex flex-col h-full">
<div
class={twMerge(
'border border-gray-300 shadow-sm divide-y divide-gray-300 h-full',
css?.container?.class ?? '',
'flex flex-col'
)}
style={css?.container?.style ?? ''}
>
{#if search !== 'Disabled'}
<div class="px-2 py-1">
<div class="flex items-center">
@@ -152,7 +169,14 @@
<div class="overflow-x-auto flex-1 w-full">
<table class="relative w-full border-b border-b-gray-200">
<thead class="sticky top-0 z-40 bg-gray-50 text-left">
<thead
class={twMerge(
'bg-gray-50 text-left',
css?.tableHeader?.class ?? '',
'sticky top-0 z-40'
)}
style={css?.tableHeader?.style ?? ''}
>
{#each $table.getHeaderGroups() as headerGroup}
<tr class="divide-x">
{#each headerGroup.headers as header}
@@ -178,7 +202,10 @@
</tr>
{/each}
</thead>
<tbody class="divide-y divide-gray-200 bg-white ">
<tbody
class={twMerge('divide-y divide-gray-200 bg-white', css?.tableBody?.class ?? '')}
style={css?.tableBody?.style ?? ''}
>
{#each $table.getRowModel().rows as row, rowIndex (row.id)}
<tr
class={classNames(
@@ -235,7 +262,13 @@
</table>
</div>
<AppTableFooter paginationEnabled={pagination} {result} {table} />
<AppTableFooter
paginationEnabled={pagination}
{result}
{table}
class={css?.tableFooter.class}
style={css?.tableFooter.style}
/>
</div>
{:else if result != undefined}
<Alert title="Parsing issues" type="error" size="xs">

View File

@@ -4,6 +4,7 @@
import type { Table } from '@tanstack/svelte-table'
import { ChevronLeft, ChevronRight } from 'lucide-svelte'
import type { Readable } from 'svelte/store'
import { twMerge } from 'tailwind-merge'
import { tableOptions } from './tableOptions'
type T = Record<string, any>
@@ -11,6 +12,9 @@
export let result: Array<T>
export let paginationEnabled: boolean = false
export let table: Readable<Table<T>>
let c = ''
export { c as class }
export let style = ''
function downloadResultAsJSON() {
const dataStr = 'data:text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(result))
@@ -23,7 +27,10 @@
}
</script>
<div class="px-2 py-1 text-xs flex flex-row gap-2 items-center justify-between">
<div
class={twMerge('px-2 py-1 text-xs gap-2 items-center justify-between', c, 'flex flex-row')}
{style}
>
{#if paginationEnabled && result.length > (tableOptions.initialState?.pagination?.pageSize ?? 25)}
<div class="flex items-center gap-2 flex-row">
<Button

View File

@@ -25,6 +25,7 @@
export let defaultUserInput = false
export let flexWrap = false
export let wrapperClass = ''
export let wrapperStyle = ''
export let initializing: boolean | undefined = undefined
export let gotoUrl: string | undefined = undefined
@@ -270,7 +271,7 @@
bind:this={testJobLoader}
/>
<div class="h-full flex relative flex-row flex-wrap {wrapperClass}">
<div class="h-full flex relative flex-row flex-wrap {wrapperClass}" style={wrapperStyle}>
{#if !initializing && autoRefresh === true}
<div class="flex absolute top-1 right-1">
<RefreshButton componentId={id} />

View File

@@ -18,6 +18,7 @@
export let defaultUserInput = false
export let flexWrap = false
export let runnableClass = ''
export let runnableStyle = ''
export let goto: string | undefined = undefined
const { staticExporter, noBackend } = getContext<AppEditorContext>('AppEditorContext')
@@ -55,6 +56,7 @@
{forceSchemaDisplay}
{initializing}
wrapperClass={runnableClass}
wrapperStyle={runnableStyle}
>
<slot />
</RunnableComponent>

View File

@@ -3,7 +3,8 @@
import { getContext } from 'svelte'
import type { AppInput } from '../../inputType'
import type { Output } from '../../rx'
import type { AppEditorContext } from '../../types'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import { concatCustomCss } from '../../utils'
import AlignWrapper from '../helpers/AlignWrapper.svelte'
import InputValue from '../helpers/InputValue.svelte'
@@ -11,8 +12,9 @@
export let configuration: Record<string, AppInput>
export let horizontalAlignment: 'left' | 'center' | 'right' | undefined = undefined
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
export let customCss: ComponentCustomCSS<'text'> | undefined = undefined
const { worldStore } = getContext<AppEditorContext>('AppEditorContext')
const { app, worldStore } = getContext<AppEditorContext>('AppEditorContext')
export const staticOutputs: string[] = ['result']
@@ -27,6 +29,8 @@
}
$: defaultValue != undefined && outputs?.result.set(defaultValue)
$: css = concatCustomCss($app.css?.checkboxcomponent, customCss)
</script>
<InputValue {id} input={configuration.label} bind:value={labelValue} />
@@ -40,6 +44,8 @@
}}
checked={defaultValue}
options={{ right: labelValue }}
textClass={css?.text?.class ?? ''}
textStyle={css?.text?.style ?? ''}
on:change={(e) => {
outputs.result.set(e.detail)
}}

View File

@@ -3,17 +3,20 @@
import Select from 'svelte-select'
import type { AppInput } from '../../inputType'
import type { Output } from '../../rx'
import type { AppEditorContext } from '../../types'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import { concatCustomCss } from '../../utils'
import AlignWrapper from '../helpers/AlignWrapper.svelte'
import InputValue from '../helpers/InputValue.svelte'
import { SELECT_INPUT_DEFAULT_STYLE } from '../../../../defaults'
export const staticOutputs: string[] = ['result']
export let id: string
export let configuration: Record<string, AppInput>
export let horizontalAlignment: 'left' | 'center' | 'right' | undefined = undefined
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
export let customCss: ComponentCustomCSS<'input'> | undefined = undefined
const { worldStore, connectingInput, selectedComponent } =
const { app, worldStore, connectingInput, selectedComponent } =
getContext<AppEditorContext>('AppEditorContext')
let items: { label: string; value: any }[]
let multiple: boolean = false
@@ -47,12 +50,12 @@
const valuePeak = outputs?.result.peak()
if (!valuePeak) {
const value0 = items?.[0]?.['value']
if (value0 != value && !items.includes(value0)) {
if (value0 != value) {
value = JSON.stringify(value0)
outputs?.result.set(value0)
}
} else {
value = valuePeak
value = JSON.stringify(valuePeak)
}
}
@@ -65,6 +68,8 @@
} catch (_) {}
outputs?.result.set(result)
}
$: css = concatCustomCss($app.css?.selectcomponent, customCss)
</script>
<InputValue {id} input={configuration.items} bind:value={items} />
@@ -72,13 +77,15 @@
<InputValue {id} input={configuration.placeholder} bind:value={placeholder} />
<AlignWrapper {horizontalAlignment} {verticalAlignment}>
<div class="app-select w-full" style="height: 34px" on:pointerdown|stopPropagation>
<div class="app-select w-full mx-0.5" style="height: 34px" on:pointerdown|stopPropagation>
<Select
--height="34px"
{multiple}
on:clear={onChange}
on:change={onChange}
items={listItems}
class={css?.input?.class ?? ''}
inputStyles={SELECT_INPUT_DEFAULT_STYLE.inputStyles}
containerStyles={SELECT_INPUT_DEFAULT_STYLE.containerStyles + css?.input.style}
{value}
{placeholder}
on:click={() => {

View File

@@ -2,7 +2,8 @@
import { getContext } from 'svelte'
import type { AppInput } from '../../inputType'
import type { Output } from '../../rx'
import type { AppEditorContext } from '../../types'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import { concatCustomCss } from '../../utils'
import AlignWrapper from '../helpers/AlignWrapper.svelte'
import InputDefaultValue from '../helpers/InputDefaultValue.svelte'
import InputValue from '../helpers/InputValue.svelte'
@@ -12,8 +13,9 @@
export let inputType = 'text'
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
export const staticOutputs: string[] = ['result']
export let customCss: ComponentCustomCSS<'input'> | undefined = undefined
const { worldStore } = getContext<AppEditorContext>('AppEditorContext')
const { app, worldStore } = getContext<AppEditorContext>('AppEditorContext')
let input: HTMLInputElement
let placeholder: string | undefined = undefined
@@ -28,6 +30,8 @@
}
$: input && handleInput()
$: css = concatCustomCss($app.css?.textinputcomponent, customCss)
</script>
<InputValue {id} input={configuration.placeholder} bind:value={placeholder} />
@@ -36,7 +40,8 @@
<AlignWrapper {verticalAlignment}>
<input
class="mx-0.5"
class="mx-0.5 {css?.input?.class ?? ''}"
style={css?.input?.style ?? ''}
on:focus={(e) => {
e?.stopPropagation()
window.dispatchEvent(new Event('pointerup'))

View File

@@ -6,4 +6,4 @@ export { default as AppNumberInput } from './AppNumberInput.svelte'
export { default as AppRangeInput } from './AppRangeInput.svelte'
export { default as AppSelect } from './AppSelect.svelte'
export { default as AppSliderInputs } from './AppSliderInputs.svelte'
export { default as AppTextInput } from './AppTextInput.svelte'
export { default as AppTextInput } from './AppTextInput.svelte'

View File

@@ -2,12 +2,14 @@
import { getContext } from 'svelte'
import SubGridEditor from '../../editor/SubGridEditor.svelte'
import type { AppInput } from '../../inputType'
import type { AppEditorContext, GridItem } from '../../types'
import type { AppEditorContext, ComponentCustomCSS, GridItem } from '../../types'
import { concatCustomCss } from '../../utils'
import InputValue from '../helpers/InputValue.svelte'
export let id: string
export let configuration: Record<string, AppInput>
export let componentContainerHeight: number
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
let noPadding: boolean | undefined = undefined
@@ -24,6 +26,8 @@
}
$: $selectedComponent === id && onFocus()
$: css = concatCustomCss($app.css?.containercomponent, customCss)
</script>
<InputValue {id} input={configuration.noPadding} bind:value={noPadding} />
@@ -32,6 +36,8 @@
{#if $app.subgrids?.[`${id}-0`]}
<SubGridEditor
{noPadding}
class={css?.container.class}
style={css?.container.style}
bind:subGrid={$app.subgrids[`${id}-0`]}
containerHeight={componentContainerHeight}
on:focus={() => {

View File

@@ -31,6 +31,15 @@
function handleTabSelection() {
outputs?.selectedTabIndex.set(selectedIndex)
if ($selectedComponent != id) {
$selectedComponent = id
}
if ($focusedGrid?.parentComponentId != id || $focusedGrid?.subGridIndex != selectedIndex) {
$focusedGrid = {
parentComponentId: id,
subGridIndex: selectedIndex
}
}
}
$: selectedIndex >= 0 && handleTabSelection()

View File

@@ -36,7 +36,6 @@
import UnsavedConfirmationModal from '$lib/components/common/confirmationModal/UnsavedConfirmationModal.svelte'
import { page } from '$app/stores'
import CssSettings from './componentsPanel/CssSettings.svelte'
import { findGridItem } from './appUtils'
export let app: App
export let path: string

View File

@@ -51,7 +51,7 @@
UserAppInput
} from '../inputType'
import type { AppEditorContext } from '../types'
import { toStatic } from '../utils'
import { allItems, toStatic } from '../utils'
import AppExportButton from './AppExportButton.svelte'
import AppInputs from './AppInputs.svelte'
import type { AppComponent } from './component/components'
@@ -124,7 +124,7 @@
}
async function computeTriggerables() {
const allTriggers = await Promise.all(
$app.grid
allItems($app.grid, $app.subgrids)
.flatMap((x) => {
let c = x.data as AppComponent
let r: (AppInput | undefined)[] = [c.componentInput]

View File

@@ -2,11 +2,16 @@
import { classNames } from '$lib/utils'
import { createEventDispatcher, getContext } from 'svelte'
import Grid from '@windmill-labs/svelte-grid'
import { twMerge } from 'tailwind-merge'
import { columnConfiguration, isFixed, toggleFixed } from '../gridUtils'
import type { AppEditorContext, GridItem } from '../types'
import Component from './component/Component.svelte'
import { findGridItem } from './appUtils'
export let containerHeight: number
let classes = ''
export { classes as class }
export let style = ''
export let noPadding = false
//export let id: string
export let subGrid: GridItem[] = []
@@ -48,7 +53,7 @@
}
function lock(gridComponent: GridItem) {
let fComponent = $app.grid.find((c) => c.id === gridComponent.id)
let fComponent = findGridItem($app, gridComponent.data.id)
if (fComponent) {
fComponent = toggleFixed(fComponent)
}
@@ -58,14 +63,16 @@
let container
</script>
<div class="relative w-full subgrid {visible ? 'visible' : 'invisible h-0 '}" bind:this={container}>
<div
class="relative w-full subgrid {visible ? 'visible' : 'invisible h-0 overflow-hidden'}"
bind:this={container}
>
<div
class:px-2={!noPadding}
class="py-2 overflow-auto {$connectingInput?.opened ? '' : ''}"
class={twMerge('py-2 overflow-auto', classes ?? '', noPadding ? 'px-0' : 'px-2')}
on:pointerdown|stopPropagation={onpointerdown}
on:pointerleave={onpointerup}
on:pointerup={onpointerup}
style="height: {containerHeight}px;"
style="height: {containerHeight}px; {style ?? ''}"
>
<div>
<Grid

View File

@@ -1,6 +1,6 @@
import { getNextId } from '$lib/components/flows/flowStateUtils'
import type { App, FocusedGrid, GridItem } from '../types'
import { Component, getRecommendedDimensionsByComponent, type AppComponent } from './component'
import { getRecommendedDimensionsByComponent, type AppComponent } from './component'
import gridHelp from '@windmill-labs/svelte-grid/src/utils/helper'
import { gridColumns } from '../gridUtils'
import { allItems } from '../utils'
@@ -10,27 +10,11 @@ function findGridItemById(
subGrids: Record<string, GridItem[]> | undefined,
id: string
): GridItem | undefined {
for (const gridItem of root) {
for (const gridItem of allItems(root, subGrids)) {
if (gridItem.id === id) {
return gridItem
}
if (subGrids) {
const numberOfSubgrids = gridItem.data.numberOfSubgrids
if (numberOfSubgrids) {
for (let i = 0; i < numberOfSubgrids; i++) {
const subgrid = subGrids[`${gridItem.id}-${i}`] ?? []
const found = findGridItemById(subgrid, subGrids, id)
if (found) {
return found
}
}
}
}
}
return undefined
}
@@ -109,8 +93,11 @@ export function insertNewGridItem(
subGrid.push(createNewGridItem(subGrid, id, data))
app.subgrids[key] = subGrid
}
for (let i = 0; i < (data.numberOfSubgrids ?? 0); i++) {
app.subgrids[`${id}-${i}`] = []
// We only want to set subgrids when we are not moving
if (!keepId) {
for (let i = 0; i < (data.numberOfSubgrids ?? 0); i++) {
app.subgrids[`${id}-${i}`] = []
}
}
return id
@@ -142,10 +129,11 @@ export function getAllSubgridsAndComponentIds(
export function deleteGridItem(
app: App,
component: AppComponent,
parent: string | undefined
parent: string | undefined,
shouldKeepSubGrid: boolean
): string[] {
let [subgrids, components] = getAllSubgridsAndComponentIds(app, component)
if (app.subgrids) {
if (app.subgrids && !shouldKeepSubGrid) {
subgrids.forEach((id) => {
delete app.subgrids![id]
})

View File

@@ -175,6 +175,9 @@ export const components: Record<AppComponent['type'], AppComponentConfig> = {
onlyStatic: true
}
},
customCss: {
container: { class: '', style: '' }
} as const,
componentInput: undefined,
card: false,
numberOfSubgrids: 1
@@ -336,7 +339,10 @@ export const components: Record<AppComponent['type'], AppComponentConfig> = {
value: ''
}
},
customCss: {},
customCss: {
container: { class: '', style: '' },
button: { class: '', style: '' }
} as const,
card: true
}
},
@@ -377,7 +383,10 @@ export const components: Record<AppComponent['type'], AppComponentConfig> = {
optionValuesKey: 'buttonSizeOptions'
}
},
customCss: {},
customCss: {
button: { class: '', style: '' },
popup: { class: '', style: '' }
} as const,
card: true
}
},
@@ -408,7 +417,9 @@ export const components: Record<AppComponent['type'], AppComponentConfig> = {
fieldType: 'object',
value: { data: [25, 50, 25], labels: ['Pie', 'Charts', '<3'] }
},
customCss: {},
customCss: {
container: { class: '', style: '' }
} as const,
card: true
}
},
@@ -439,7 +450,9 @@ export const components: Record<AppComponent['type'], AppComponentConfig> = {
fieldType: 'object',
value: { data: [25, 50, 25], labels: ['Bar', 'Charts', '<3'] }
},
customCss: {},
customCss: {
container: { class: '', style: '' }
} as const,
card: true
}
},
@@ -462,7 +475,9 @@ Hello \${ctx.username}
</h1>`
},
configuration: {},
customCss: {},
customCss: {
container: { class: '', style: '' }
} as const,
card: false
}
},
@@ -604,7 +619,9 @@ Hello \${ctx.username}
}
]
},
customCss: {},
customCss: {
container: { class: '', style: '' }
} as const,
card: true
}
},
@@ -654,7 +671,9 @@ Hello \${ctx.username}
}
]
},
customCss: {},
customCss: {
container: { class: '', style: '' }
} as const,
card: true
}
},
@@ -691,7 +710,12 @@ Hello \${ctx.username}
}
]
},
customCss: {},
customCss: {
container: { class: '', style: '' },
tableHeader: { class: '', style: '' },
tableBody: { class: '', style: '' },
tableFooter: { class: '', style: '' }
} as const,
card: true,
actionButtons: []
}
@@ -715,6 +739,18 @@ Hello \${ctx.username}
fieldType: 'boolean',
value: false,
onlyStatic: true
},
pagination: {
type: 'static',
fieldType: 'boolean',
value: false,
onlyStatic: true
},
pageSize: {
type: 'static',
fieldType: 'number',
value: 10,
onlyStatic: true
}
},
componentInput: {
@@ -735,7 +771,7 @@ Hello \${ctx.username}
]
},
customCss: {},
card: true,
card: true
}
},
checkboxcomponent: {
@@ -760,7 +796,9 @@ Hello \${ctx.username}
fieldType: 'boolean'
}
},
customCss: {},
customCss: {
text: { class: '', style: '' }
} as const,
card: false
}
},
@@ -787,7 +825,9 @@ Hello \${ctx.username}
fieldType: 'text'
}
},
customCss: {},
customCss: {
input: { class: '', style: '' }
} as const,
card: false
}
},
@@ -823,7 +863,9 @@ Hello \${ctx.username}
onlyStatic: true
}
},
customCss: {},
customCss: {
input: { style: '' }
} as const,
card: false,
softWrap: true
}
@@ -853,17 +895,17 @@ Hello \${ctx.username}
min: {
type: 'static',
value: undefined,
fieldType: 'number',
fieldType: 'number'
},
max: {
type: 'static',
value: undefined,
fieldType: 'number',
fieldType: 'number'
},
step: {
type: 'static',
value: 1,
fieldType: 'number',
fieldType: 'number'
}
},
customCss: {},
@@ -931,17 +973,17 @@ Hello \${ctx.username}
max: {
type: 'static',
value: 42,
fieldType: 'number',
fieldType: 'number'
},
defaultValue: {
type: 'static',
value: 20,
fieldType: 'number',
fieldType: 'number'
},
step: {
type: 'static',
value: 1,
fieldType: 'number',
fieldType: 'number'
}
},
customCss: {},
@@ -962,27 +1004,27 @@ Hello \${ctx.username}
min: {
type: 'static',
value: 0,
fieldType: 'number',
fieldType: 'number'
},
max: {
type: 'static',
value: 42,
fieldType: 'number',
fieldType: 'number'
},
defaultLow: {
type: 'static',
value: 10,
fieldType: 'number',
fieldType: 'number'
},
defaultHigh: {
type: 'static',
value: 20,
fieldType: 'number',
fieldType: 'number'
},
step: {
type: 'static',
value: 1,
fieldType: 'number',
fieldType: 'number'
}
},
customCss: {},

View File

@@ -1,34 +1,59 @@
<script lang="ts">
import type { ComponentCssProperty } from "../../types"
import { X } from 'lucide-svelte'
import { fade } from 'svelte/transition'
import { addWhitespaceBeforeCapitals } from '../../../../utils'
import type { ComponentCssProperty } from '../../types'
export let name: string
export let value: ComponentCssProperty | undefined
function reset(property: keyof ComponentCssProperty) {
if (value) {
value[property] = ''
}
}
</script>
<div class="text-sm font-semibold text-gray-500 capitalize pt-2">
{name}
{addWhitespaceBeforeCapitals(name)}
</div>
{#if value}
<div class="border-l border-gray-400/80 py-1 pl-3.5 mt-1 ml-0.5">
<label class="block pb-2">
<div class="text-xs font-medium pb-0.5">
Style
</div>
<input
type="text"
bind:value={value.style}
on:focus
/>
</label>
<label class="block">
<div class="text-xs font-medium pb-0.5">
Tailwind classes
</div>
<input
type="text"
bind:value={value.class}
on:focus
/>
</label>
{#if value.style !== undefined}
<label class="block pb-2">
<div class="text-xs font-medium pb-0.5"> Style </div>
<div class="relative">
<input type="text" class="!pr-7" bind:value={value.style} on:focus />
{#if value?.style}
<button
transition:fade={{ duration: 100 }}
class="absolute z-10 top-1.5 right-1 rounded-full p-1 duration-200 hover:bg-gray-200"
aria-label="Remove styles"
on:click|preventDefault|stopPropagation={() => reset('style')}
>
<X size={14} />
</button>
{/if}
</div>
</label>
{/if}
{#if value.class !== undefined}
<label class="block">
<div class="text-xs font-medium pb-0.5"> Tailwind classes </div>
<div class="relative">
<input type="text" class="!pr-7" bind:value={value.class} on:focus />
{#if value?.class}
<button
transition:fade={{ duration: 100 }}
class="absolute z-10 top-1.5 right-1 rounded-full p-1 duration-200 hover:bg-gray-200"
aria-label="Remove classes"
on:click|preventDefault|stopPropagation={() => reset('class')}
>
<X size={14} />
</button>
{/if}
</div>
</label>
{/if}
</div>
{/if}
{/if}

View File

@@ -9,15 +9,21 @@
import { isOpenStore } from './store'
import CssProperty from './CssProperty.svelte'
import { components, type AppComponent } from '../component'
import { slide } from 'svelte/transition'
const STATIC_ELEMENTS = ['app'] as const
const TITLE_PREFIX = 'Css.' as const
const SHOW_UNUSED_ID = 'Setting.ShowUnused' as const
type CustomCSSType = (typeof STATIC_ELEMENTS)[number] | AppComponent['type']
interface CustomCSSEntry {
type: 'app' | AppComponent['type'];
name: string;
icon: any;
ids: string[];
type: CustomCSSType
name: string
icon: any
ids: string[]
}
const TITLE_PREFIX = 'Css.' as const
const { app } = getContext<AppEditorContext>('AppEditorContext')
let rawCode = ''
@@ -51,7 +57,7 @@
icon: LayoutDashboardIcon,
ids: ['viewer', 'grid', 'component']
},
...Object.values(components).map(({ name, icon, data: { type, customCss }}) => ({
...Object.values(components).map(({ name, icon, data: { type, customCss } }) => ({
type,
name,
icon,
@@ -70,7 +76,7 @@
}
e.ids.forEach((id) => {
if (!newCss[e.type][id]) {
newCss[e.type][id] = { style: '', class: '' }
newCss[e.type][id] = components?.[e.type]?.data?.customCss?.[id] ?? { class: '', style: '' }
}
})
e.ids
@@ -84,20 +90,32 @@
//@ts-ignore
$app.css = newCss
$: staticComponents = entries.filter(({ type }) => STATIC_ELEMENTS.includes(type as any))
$: usedComponents = Object.keys(
$app.grid.reduce((acc, { data }) => {
acc[data.type] = true
return acc
}, {})
)
.map((type) => entries.find((entry) => entry.type === type))
.filter(Boolean) as CustomCSSEntry[]
$: unusedComponents = entries.filter(({ type }) => {
return (
!STATIC_ELEMENTS.includes(type as any) && !usedComponents.find((entry) => entry.type === type)
)
})
onMount(() => {
isOpenStore.addItems(
[{ name: 'App' }, ...Object.values(components)].map(component => {
[{ name: 'App' }, ...Object.values(components)].map((component) => {
return { [TITLE_PREFIX + component.name]: false }
})
)
isOpenStore.addItems([{ [SHOW_UNUSED_ID]: false }])
})
</script>
<Tabs
selected="ui"
on:selected={(e) => switchTab(e.detail === 'json')}
class="relative"
>
<Tabs selected="ui" on:selected={(e) => switchTab(e.detail === 'json')} class="relative">
<Tab value="ui" size="xs" class="grow">
<div class="m-1 center-center">
<MousePointer2 size={16} />
@@ -112,7 +130,7 @@
</Tab>
<div slot="content" class="h-full overflow-y-auto">
<TabContent value="ui">
{#each entries as { type, name, icon, ids }}
{#each [...staticComponents, ...usedComponents] as { type, name, icon, ids }}
{#if ids.length > 0}
<ListItem title={name} prefix={TITLE_PREFIX}>
<div slot="title" class="flex items-center">
@@ -137,6 +155,44 @@
</ListItem>
{/if}
{/each}
<div class="px-1 my-4">
<button
on:click|preventDefault|stopPropagation={() => isOpenStore.toggle(SHOW_UNUSED_ID)}
class="w-full text-xs text-gray-500 text-center font-medium hover:underline p-2"
>
{$isOpenStore[SHOW_UNUSED_ID] ? 'Hide' : 'Show'} unused components
</button>
</div>
{#if $isOpenStore[SHOW_UNUSED_ID]}
<div transition:slide|local={{ duration: 300 }}>
{#each unusedComponents as { type, name, icon, ids }}
{#if ids.length > 0}
<ListItem title={name} prefix={TITLE_PREFIX}>
<div slot="title" class="flex items-center">
<svelte:component this={icon} size={18} />
<span class="ml-1">
{name}
</span>
</div>
<div class="pb-2">
{#each ids as id}
<div class="mb-3">
{#if $app?.css?.[type][id]}
{JSON.stringify($app.css[type][id])}
<CssProperty
name={id}
bind:value={$app.css[type][id]}
on:focus={() => (isCustom[type] = true)}
/>
{/if}
</div>
{/each}
</div>
</ListItem>
{/if}
{/each}
</div>
{/if}
</TabContent>
<TabContent value="json">
{#if !emptyString(jsonError)}

View File

@@ -10,7 +10,7 @@
$: isOpen = $isOpenStore[storeTitle]
</script>
<section class="mt-1 mb-2 px-1">
<section class="pt-1 pb-2 px-1">
<button
on:click|preventDefault={() => isOpenStore.toggle(storeTitle)}
class="w-full flex justify-between items-center text-gray-700 px-2 py-1

View File

@@ -1,10 +1,9 @@
<script lang="ts">
import Button from '$lib/components/common/button/Button.svelte'
import type { Preview } from '$lib/gen'
import { faTrash } from '@fortawesome/free-solid-svg-icons'
import { createEventDispatcher, getContext, onMount } from 'svelte'
import type { AppEditorContext, InlineScript } from '../../types'
import { CheckCircle, Code2, Maximize2, Trash2, X } from 'lucide-svelte'
import { CheckCircle, Maximize2, Trash2, X } from 'lucide-svelte'
import InlineScriptEditorDrawer from './InlineScriptEditorDrawer.svelte'
import { inferArgs } from '$lib/infer'
import type { Schema } from '$lib/common'

View File

@@ -21,7 +21,7 @@
}
}
$: runnables = getAppScripts($lazyGrid)
$: runnables = getAppScripts($lazyGrid, $app.subgrids)
// When selected component changes, update selectedScriptComponentId
$: if ($selectedComponent != selectedScriptComponentId) {

View File

@@ -1,11 +1,11 @@
import type { Schema } from "$lib/common";
import type { AppInputs, Runnable } from "../../inputType"
import type { GridItem } from "../../types"
import { fieldTypeToTsType, schemaToInputsSpec } from "../../utils";
import type { AppComponent } from "../component";
import type { Schema } from '$lib/common'
import type { AppInputs, Runnable } from '../../inputType'
import type { GridItem } from '../../types'
import { fieldTypeToTsType, schemaToInputsSpec } from '../../utils'
import type { AppComponent } from '../component'
export interface AppScriptsList {
inline: { name: string; id: string }[],
inline: { name: string; id: string }[]
imported: { name: string; id: string }[]
}
@@ -42,38 +42,50 @@ export function computeFields(schema: Schema, defaultUserInput: boolean, fields:
return result
}
export function getAppScripts(grid: GridItem[]) {
return grid.reduce((acc, gridComponent) => {
const component: AppComponent = gridComponent.data
const componentInput = component.componentInput
function processGridItemRunnable(gridItem: GridItem, list: AppScriptsList): AppScriptsList {
const component: AppComponent = gridItem.data
const componentInput = component.componentInput
if (component.type === 'tablecomponent') {
component.actionButtons.forEach((actionButton) => {
if (actionButton.componentInput?.type !== 'runnable') {
return
}
processRunnable(actionButton.componentInput.runnable, actionButton.id, list)
})
}
if (componentInput?.type === 'runnable') {
processRunnable(componentInput.runnable, gridItem.id, list)
}
return list
}
if (component.type === 'tablecomponent') {
component.actionButtons.forEach((actionButton) => {
if (actionButton.componentInput?.type !== 'runnable') { return }
processRunnable(
actionButton.componentInput.runnable,
actionButton.id,
acc
)
export function getAppScripts(
lazyGrid: GridItem[],
subgrids: Record<string, GridItem[]> | undefined
): AppScriptsList {
const scriptsList = lazyGrid.reduce(
(acc, gridComponent) => processGridItemRunnable(gridComponent, acc),
{ inline: [], imported: [] } as AppScriptsList
)
if (subgrids) {
Object.values(subgrids).forEach((subgrid: GridItem[]) => {
subgrid.forEach((subgridComponent: GridItem) => {
processGridItemRunnable(subgridComponent, scriptsList)
})
}
if (componentInput?.type === 'runnable') {
processRunnable(
componentInput.runnable,
gridComponent.id,
acc
)
}
})
}
return acc
}, { inline: [], imported: [] } as AppScriptsList)
return scriptsList
}
function processRunnable(runnable: Runnable, id: string, list: AppScriptsList) {
if (runnable?.type === undefined) { return }
if (runnable?.type === undefined) {
return
}
const type: keyof AppScriptsList = runnable.type === 'runnableByPath' ? 'imported' : 'inline'
list[type].push({
name: runnable[runnable.type === 'runnableByPath' ? 'path' : 'name'],
id
})
}
}

View File

@@ -44,7 +44,7 @@
$selectedComponent = undefined
$focusedGrid = undefined
if (component && !noGrid) {
let ids = deleteGridItem($app, component, parent)
let ids = deleteGridItem($app, component, parent, false)
for (const key of ids) {
delete $staticOutputs[key]
delete $runnableComponents[key]
@@ -166,7 +166,7 @@
<PanelSection title="Custom CSS">
{#each Object.keys(component.customCss ?? {}) as name}
{#if component?.customCss?.[name]}
<div class="mb-2">
<div class="w-full mb-2">
<CssProperty {name} bind:value={component.customCss[name]} />
</div>
{/if}

View File

@@ -28,7 +28,7 @@
function deleteSubgrid(index: number) {
let subgrid = `${component.id}-${index}`
for (const item of $app!.subgrids![subgrid]) {
const components = deleteGridItem($app, item.data, subgrid)
const components = deleteGridItem($app, item.data, subgrid, false)
console.log(components)
for (const key in components) {
delete $staticOutputs[key]

View File

@@ -4,11 +4,18 @@
import { faCopy } from '@fortawesome/free-solid-svg-icons'
import { getContext } from 'svelte'
import type { App, AppEditorContext } from '../../types'
import { createNewGridItem, deleteGridItem, findGridItem, insertNewGridItem } from '../appUtils'
import {
createNewGridItem,
deleteGridItem,
findGridItem,
getAllSubgridsAndComponentIds,
insertNewGridItem
} from '../appUtils'
import type { AppComponent } from '../component'
export let component: AppComponent | undefined
export let parent: string | undefined
let selectedOption: string
const { app } = getContext<AppEditorContext>('AppEditorContext')
@@ -25,9 +32,12 @@
}
const data: AppComponent = JSON.parse(JSON.stringify(node.data))
const shouldKeepSubGrid = data.numberOfSubgrids ? data.numberOfSubgrids >= 1 : false
$dirtyStore = true
deleteGridItem($app, data, parent)
deleteGridItem($app, data, parent, shouldKeepSubGrid)
return data
}
@@ -36,15 +46,13 @@
targetId: string,
targetSubGridIndex: number
) {
insertNewGridItem(
$app,
data,
{
parentComponentId: targetId,
subGridIndex: targetSubGridIndex
},
true
)
const focusedGrid = {
parentComponentId: targetId,
subGridIndex: targetSubGridIndex
}
insertNewGridItem($app, data, focusedGrid, true)
$app.grid = [...$app.grid]
}
@@ -73,6 +81,8 @@
value: 'main-grid',
disabled: parent === undefined
}
$: [subgrids] = component ? getAllSubgridsAndComponentIds($app, component) : [[], []]
$: availableGrids = listAllSubGrids($app)
$: options = availableGrids
? [
@@ -80,7 +90,7 @@
...availableGrids?.map((grid) => ({
label: grid,
value: grid,
disabled: grid === parent || (component && grid.startsWith(component.id))
disabled: grid === parent || subgrids.includes(grid)
}))
]
: [defaultOption]

View File

@@ -30,8 +30,8 @@ export interface GeneralAppInput {
}
export type ComponentCssProperty = {
class: string
style: string
class?: string
style?: string
}
export type ComponentCustomCSS<T extends string = string> = Record<T, ComponentCssProperty>

View File

@@ -3,49 +3,14 @@ import { FlowService, ScriptService } from '$lib/gen'
import { inferArgs } from '$lib/infer'
import { emptySchema } from '$lib/utils'
import type { AppComponent } from './editor/component'
import type { App, ComponentCssProperty, ComponentCustomCSS, GridItem } from './types'
import { getRecommendedDimensionsByComponent } from './editor/component'
import { gridColumns } from './gridUtils'
import gridHelp from '@windmill-labs/svelte-grid/src/utils/helper'
import { twMerge } from 'tailwind-merge'
import type { AppInput, InputType, ResultAppInput, StaticAppInput } from './inputType'
import type { Output } from './rx'
import type { App, GridItem } from './types'
/*
export function deleteComponent(
subgrid: string | undefined,
component: AppComponent,
app: App,
staticOutputs: Record<string, any>,
runnableComponents: Record<string, any>
) {
if (parentItems) {
let index = parentItems.findIndex((item) => item.data?.id === component.id)
if (index != -1) {
parentItems.splice(index, 1)
}
}
delete staticOutputs[component.id]
delete runnableComponents[component.id]
if (
component.componentInput?.type === 'runnable' &&
component.componentInput?.runnable?.type === 'runnableByName'
) {
const { name, inlineScript } = component.componentInput.runnable
if (inlineScript) {
if (!app.unusedInlineScripts) {
app.unusedInlineScripts = []
}
app.unusedInlineScripts.push({
name,
inlineScript
})
}
}
}
*/
import { getNextId } from '../flows/flowStateUtils'
export function allItems(
grid: GridItem[],
@@ -273,3 +238,128 @@ export function toPascalCase(text: string) {
export function toKebabCase(text: string) {
return text.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()
}
export function findParent(root: GridItem[], id: string): GridItem | undefined {
for (const a of root) {
if (a.id === id) {
return a
}
if (a.data.subGrids) {
// Recursively search the sub-grids
for (const subGrid of a.data.subGrids) {
const result = findParent(subGrid, id)
if (result) {
return result
}
}
}
}
return undefined
}
export function insertNewGridItem(
root: GridItem[],
id: string,
subGridIndex: number,
newId: string,
data: AppComponent
): GridItem[] {
const parentA = findParent(root, id)
if (!parentA) {
throw new Error(`Parent A object with ID ${id} not found.`)
}
const subGrid = parentA.data.subGrids[subGridIndex]
if (!subGrid) {
throw new Error(`Sub-grid with index ${subGridIndex} not found.`)
}
const newItem = createNewGridItem(subGrid ?? [], newId, data)
subGrid.push(newItem)
return root
}
// The grid is needed to find a space for the new component
export function createNewGridItem(grid: GridItem[], id: string, data: AppComponent): GridItem {
const appComponent = data
appComponent.id = id
const newComponent = {
fixed: false,
resizable: true,
draggable: true,
customDragger: false,
customResizer: false,
x: 0,
y: 0
}
let newData: AppComponent = JSON.parse(JSON.stringify(appComponent))
const newItem: GridItem = {
data: newData,
id: id
}
gridColumns.forEach((column) => {
const rec = getRecommendedDimensionsByComponent(appComponent.type, column)
newItem[column] = {
...newComponent,
min: { w: 1, h: 1 },
max: { w: column, h: 100 },
w: rec.w,
h: rec.h
}
const position = gridHelp.findSpace(newItem, grid, column) as { x: number; y: number }
newItem[column] = { ...newItem[column], ...position }
})
return newItem
}
function recursiveGetIds(gridItem: GridItem): string[] {
const subGrids = gridItem.data.subGrids ?? []
const subGridIds = subGrids
.map((subGrid: GridItem[]) => subGrid.map(recursiveGetIds))
.flat(Infinity)
return [gridItem.data.id, ...subGridIds]
}
export function getNextGridItemId(grid: GridItem[] = []): string {
const gridItemIds = grid.map(recursiveGetIds).flat()
const id = getNextId(gridItemIds)
return id
}
export function concatCustomCss<T extends string = string>(
appCss?: ComponentCustomCSS<T>,
componentCss?: ComponentCustomCSS<T>
): Record<T, ComponentCssProperty> | undefined {
if (!componentCss) return undefined
const customStyle = {} as Record<T, ComponentCssProperty>
Object.keys(componentCss).forEach((key) => {
const k = key as keyof typeof componentCss
// This is the general style of the component type
const appStyle = appCss?.[k]?.style?.trim() || ''
const appEnding = appStyle?.endsWith(';') || !appStyle ? ' ' : '; '
// This is the custom style of the component instance
const compStyle = componentCss[k]?.style?.trim() || ''
const compEnding = compStyle?.endsWith(';') || !compStyle ? ' ' : ';'
customStyle[k] = {
style: (appStyle + appEnding + compStyle + compEnding).trim(),
class: twMerge(appCss?.[k]?.class, componentCss[k]?.class)
}
})
return customStyle
}

View File

@@ -1,12 +1,15 @@
<script lang="ts">
import { classNames } from '$lib/utils'
import { createEventDispatcher } from 'svelte'
import { fade } from 'svelte/transition'
import Button from '../button/Button.svelte'
import Badge from '../badge/Badge.svelte'
import { twMerge } from 'tailwind-merge'
export let title: string
export let open: boolean = false
let c: string = ''
export { c as class }
export let style = ''
const dispatch = createEventDispatcher()
@@ -40,7 +43,7 @@
role="dialog"
>
<div
class={classNames(
class={twMerge(
'fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity',
open ? 'ease-out duration-300 opacity-100' : 'ease-in duration-200 opacity-0'
)}
@@ -49,12 +52,14 @@
<div class="fixed inset-0 z-10 overflow-y-auto">
<div class="flex min-h-full items-center justify-center p-4">
<div
class={classNames(
class={twMerge(
'relative transform overflow-hidden rounded-lg bg-white px-4 pt-5 pb-4 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg sm:p-6',
c,
open
? 'ease-out duration-300 opacity-100 translate-y-0 sm:scale-100'
: 'ease-in duration-200 opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95'
)}
{style}
>
<div class="flex">
<div class="ml-4 text-left flex-1">

View File

@@ -27,8 +27,7 @@
parentModule.id,
$flowStore,
$previewArgs,
false,
true
false
)
</script>
@@ -37,6 +36,7 @@
pickableProperties={stepPropPicker.pickableProperties}
on:select={({ detail }) => {
editor?.insertAtCursor(detail)
editor?.focus()
}}
>
<div class="border border-gray-400">

View File

@@ -33,8 +33,7 @@
mod.id,
$flowStore,
$previewArgs,
false,
true
false
)
</script>
@@ -60,6 +59,7 @@
pickableProperties={stepPropPicker.pickableProperties}
on:select={({ detail }) => {
editor?.insertAtCursor(detail)
editor?.focus()
}}
>
<SimpleEditor

View File

@@ -12,7 +12,12 @@
import FlowCard from '../common/FlowCard.svelte'
import FlowModuleHeader from './FlowModuleHeader.svelte'
import { flowStateStore } from '../flowState'
import { schemaToObject, scriptLangToEditorLang } from '$lib/utils'
import {
getLatestHashForScript,
getScriptByPath,
schemaToObject,
scriptLangToEditorLang
} from '$lib/utils'
import PropPickerWrapper from '../propPicker/PropPickerWrapper.svelte'
import { afterUpdate, getContext } from 'svelte'
import type { FlowEditorContext } from '../types'
@@ -71,8 +76,7 @@
flowModule.id,
$flowStore,
$previewArgs,
false,
true
false
)
function onKeyDown(event: KeyboardEvent) {
@@ -116,6 +120,8 @@
let isScript = true
$: isScript != (value.type === 'script') && (isScript = value.type === 'script')
let forceReload = 0
</script>
<svelte:window on:keydown={onKeyDown} />
@@ -135,6 +141,16 @@
flowModule = module
$flowStateStore[module.id] = state
}}
on:reload={async () => {
if (flowModule.value.type == 'script') {
console.log('reload')
if (flowModule.value.hash != undefined) {
flowModule.value.hash = await getLatestHashForScript(flowModule.value.path)
}
forceReload++
await reload(flowModule)
}
}}
on:createScriptFromInlineScript={async () => {
const [module, state] = await createScriptFromInlineScript(
flowModule,
@@ -194,7 +210,9 @@
/>
</div>
{:else if value.type === 'script'}
<FlowModuleScript path={value.path} hash={value.hash} />
{#key forceReload}
<FlowModuleScript path={value.path} hash={value.hash} />
{/key}
{:else if value.type === 'flow'}
<FlowPathViewer path={value.path} />
{/if}

View File

@@ -57,6 +57,7 @@
pickableProperties={undefined}
on:select={({ detail }) => {
editor?.insertAtCursor(detail)
editor?.focus()
}}
>
<SimpleEditor

View File

@@ -1,12 +1,15 @@
<script lang="ts">
import Button from '$lib/components/common/button/Button.svelte'
import type { FlowModule } from '$lib/gen'
import { faCodeBranch, faSave } from '@fortawesome/free-solid-svg-icons'
import { createEventDispatcher } from 'svelte'
import { faCodeBranch, faPen, faSave } from '@fortawesome/free-solid-svg-icons'
import { createEventDispatcher, getContext } from 'svelte'
import { Bed, PhoneIncoming, Repeat, Square } from 'lucide-svelte'
import Popover from '../../Popover.svelte'
import type { FlowEditorContext } from '../types'
import { getLatestHashForScript, sendUserToast } from '$lib/utils'
export let module: FlowModule
const { scriptEditorDrawer } = getContext<FlowEditorContext>('FlowEditorContext')
const dispatch = createEventDispatcher()
@@ -62,6 +65,27 @@
{/if}
{#if module.value.type === 'script'}
<div class="w-2" />
{#if !module.value.path.startsWith('hub/')}
<Button
size="xs"
color="light"
variant="border"
on:click={async () => {
if (module.value.type == 'script') {
const hash = module.value.hash ?? (await getLatestHashForScript(module.value.path))
$scriptEditorDrawer?.openDrawer(hash, () => {
dispatch('reload')
sendUserToast('Script has been updated')
})
}
}}
startIcon={{ icon: faPen }}
iconOnly={false}
disabled={module.value.hash != undefined}
>
Edit {#if module.value.hash != undefined} (locked hash){/if}
</Button>
{/if}
<Button
size="xs"
color="light"

View File

@@ -64,6 +64,7 @@
pickableProperties={undefined}
on:select={({ detail }) => {
editor?.insertAtCursor(detail)
editor?.focus()
}}
>
<InputTransformForm

View File

@@ -0,0 +1,117 @@
<script lang="ts">
import { Button, Drawer, DrawerContent } from '$lib/components/common'
import ScriptEditor from '$lib/components/ScriptEditor.svelte'
import { ScriptService, type Preview } from '$lib/gen'
import { inferArgs } from '$lib/infer'
import { workspaceStore } from '$lib/stores'
import { emptySchema, getScriptByPath, sendUserToast } from '$lib/utils'
import { faSave } from '@fortawesome/free-solid-svg-icons'
import { Loader2 } from 'lucide-svelte'
import { createEventDispatcher } from 'svelte'
import { fade } from 'svelte/transition'
let scriptEditorDrawer: Drawer
const dispatch = createEventDispatcher()
export async function openDrawer(hash: string, cb: () => void): Promise<void> {
script = undefined
scriptEditorDrawer.openDrawer?.()
script = await ScriptService.getScriptByHash({
workspace: $workspaceStore!,
hash
})
callback = cb
}
let callback: (() => void) | undefined = undefined
let script:
| {
path: string
description: string
summary: string
hash: string
language: Preview.language
content: string
schema?: any
kind: 'script' | 'failure' | 'trigger' | 'command' | 'approval' | undefined
}
| undefined = undefined
async function saveScript(): Promise<void> {
if (script) {
try {
script.schema = script.schema ?? emptySchema()
try {
await inferArgs(script.language, script.content, script.schema)
} catch (error) {
sendUserToast(
`Impossible to infer the schema. Assuming this is a script without main function`,
true
)
}
await ScriptService.createScript({
workspace: $workspaceStore!,
requestBody: {
path: script.path,
summary: script.summary,
description: script.description ?? '',
content: script.content,
parent_hash: script.hash != '' ? script.hash : undefined,
schema: script.schema,
is_template: false,
language: script.language,
kind: script.kind
}
})
callback?.()
} catch (error) {
sendUserToast(`Impossible to save the script: ${error.body}`, true)
}
}
}
</script>
<Drawer bind:this={scriptEditorDrawer} size="1200px">
<DrawerContent
title="Script Editor"
noPadding
forceOverflowVisible
on:close={() => {
scriptEditorDrawer.closeDrawer()
}}
>
{#if script}
{#key script.hash}
<ScriptEditor
noSyncFromGithub
lang={script.language}
path={script.path}
fixedOverflowWidgets={false}
bind:code={script.content}
bind:schema={script.schema}
/>
{/key}
{:else}
<div
out:fade={{ duration: 200 }}
class="absolute inset-0 center-center flex-col bg-white text-gray-600 border"
>
<Loader2 class="animate-spin" size={16} />
<span class="text-xs mt-1">Loading</span>
</div>
{/if}
<svelte:fragment slot="actions">
<Button
on:click={async () => {
await saveScript()
dispatch('save')
scriptEditorDrawer.closeDrawer()
}}
disabled={!script}
startIcon={{ icon: faSave }}>Save</Button
>
</svelte:fragment>
</DrawerContent>
</Drawer>

View File

@@ -85,7 +85,7 @@
</div>
{/if}
{#if displayLock}
<div class="flex flex-row-reverse">
<div class="flex flex-row-reverse mb-1">
<Toggle
bind:checked={lockHash}
options={{ left: 'Latest version', right: 'Lock current hash permanently' }}

View File

@@ -103,7 +103,6 @@ export function getStepPropPicker(
flow: Flow,
args: any,
include_node: boolean,
approvers: boolean = false
): StepPropPicker {
const flowInput = getFlowInput(dfs(parentModule?.id, flow), flowState, args, flow.schema)

View File

@@ -1,4 +1,5 @@
import type { Writable } from 'svelte/store'
import type ScriptEditorDrawer from './content/ScriptEditorDrawer.svelte'
import type { Schedule } from './scheduleUtils'
export type FlowEditorContext = {
@@ -6,4 +7,5 @@ export type FlowEditorContext = {
select: (id: string) => void
schedule: Writable<Schedule>,
previewArgs: Writable<Record<string, any>>,
scriptEditorDrawer: Writable<ScriptEditorDrawer | undefined>
}

View File

@@ -73,7 +73,7 @@ export async function loadSchemaFromModule(module: FlowModule): Promise<{
schema = emptySchema()
await inferArgs(mod.language!, mod.content ?? '', schema)
} else if (mod.type == 'script' && mod.path && mod.path != '') {
schema = await loadSchema(mod.path!)
schema = await loadSchema(mod.path!, mod.hash)
} else if (mod.type == 'flow' && mod.path && mod.path != '') {
schema = await loadSchemaFlow(mod.path!)
} else {

View File

@@ -51,6 +51,7 @@ export type GraphItem = Node | Loop | Branch
export type GraphModuleState = {
type: FlowStatusModule.type
args: any
logs?: string
result?: any
scheduled_for?: string

View File

@@ -13,11 +13,17 @@
function getIconComponent(name: string) {
return APP_TO_ICON_COMPONENT[name] || APP_TO_ICON_COMPONENT[name.split('_')[0]]
}
$: filtersAndSelected = selectedFilter
? filters.includes(selectedFilter)
? filters
: [selectedFilter, ...filters]
: filters
</script>
{#if Array.isArray(filters) && filters.length > 0}
{#if Array.isArray(filtersAndSelected) && filtersAndSelected.length > 0}
<div class="gap-2 w-full flex flex-wrap my-4">
{#each filters as filter (filter)}
{#each filtersAndSelected as filter (filter)}
<div in:fade={{ duration: 50 }} animate:flip={{ duration: 100 }}>
<Badge
class={classNames(

View File

@@ -0,0 +1,5 @@
export const SELECT_INPUT_DEFAULT_STYLE = {
inputStyles: 'border-radius: 0;',
containerStyles:
'--height: 34px; --padding: 0 0 0 8px; --font-size: 0.875rem; --border: 1px solid #d1d5db;'
} as const

View File

@@ -5,7 +5,7 @@ import { inferArgs } from './infer'
import { workspaceStore } from './stores'
import { emptySchema } from './utils'
export async function loadSchema(path: string): Promise<Schema> {
export async function loadSchema(path: string, hash?: string): Promise<Schema> {
if (path.startsWith('hub/')) {
const { content, language, schema } = await ScriptService.getHubScriptByPath({ path })
if (language == 'deno') {
@@ -15,6 +15,12 @@ export async function loadSchema(path: string): Promise<Schema> {
} else {
return schema ?? emptySchema()
}
} else if (hash) {
const script = await ScriptService.getScriptByHash({
workspace: get(workspaceStore)!,
hash
})
return script.schema
} else {
const script = await ScriptService.getScriptByPath({
workspace: get(workspaceStore)!,

View File

@@ -511,6 +511,14 @@ export async function getScriptByPath(path: string): Promise<{
}
}
export async function getLatestHashForScript(path: string): Promise<string> {
const script = await ScriptService.getScriptByPath({
workspace: get(workspaceStore)!,
path: path ?? ''
})
return script.hash
}
export async function loadHubScripts() {
try {
const scripts = (await ScriptService.listHubScripts()).asks ?? []

View File

@@ -103,7 +103,7 @@
}
}
} else {
console.log('Caught unhandled promise rejection without message', event)
console.error('Caught unhandled promise rejection without message', event.reason, event)
}
}
loadUser()

View File

@@ -1,4 +1,5 @@
const plugin = require('tailwindcss/plugin')
const colors = require('tailwindcss/colors')
/** @type {import('tailwindcss').Config} */
const config = {
@@ -13,6 +14,7 @@ const config = {
],
theme: {
colors: {
...colors,
current: 'currentcolor',
transparent: 'transparent',
white: '#ffffff',

View File

@@ -4,8 +4,8 @@ verify_ssl = true
name = "pypi"
[packages]
wmill = ">=1.69.2"
wmill_pg = ">=1.69.2"
wmill = ">=1.70.1"
wmill_pg = ">=1.70.1"
sendgrid = "*"
mysql-connector-python = "*"
pymongo = "*"

View File

@@ -1,7 +1,7 @@
openapi: "3.0.3"
info:
version: 1.69.2
version: 1.70.1
title: OpenFlow Spec
contact:
name: Ruben Fiszel

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "wmill"
version = "1.69.2"
version = "1.70.1"
description = "A client library for accessing Windmill server wrapping the Windmill client API"
license = "Apache-2.0"
homepage = "https://windmill.dev"
@@ -16,7 +16,7 @@ include = ["wmill/py.typed"]
[tool.poetry.dependencies]
python = "^3.7"
windmill-api = "^1.69.2"
windmill-api = "^1.70.1"
[build-system]
requires = ["poetry>=1.0.2", "poetry-dynamic-versioning"]

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "wmill-pg"
version = "1.69.2"
version = "1.70.1"
description = "An extension client for the wmill client library focused on pg"
license = "Apache-2.0"
homepage = "https://windmill.dev"

View File

@@ -1 +1 @@
1.69.3
1.70.1