Compare commits
57 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
020e5f7b32 | ||
|
|
1414030afe | ||
|
|
92bf928b78 | ||
|
|
0bcc036a84 | ||
|
|
6277188c1b | ||
|
|
65c9d43419 | ||
|
|
cc5744ee2d | ||
|
|
1e796881b3 | ||
|
|
a55d99a8b7 | ||
|
|
07477f9e35 | ||
|
|
9409d5e266 | ||
|
|
2d71ebbe09 | ||
|
|
c1673ac036 | ||
|
|
8ca54e02c0 | ||
|
|
cd25570003 | ||
|
|
e95f8ef6bf | ||
|
|
c3d1c8ac39 | ||
|
|
d38aff2fe2 | ||
|
|
95851ea486 | ||
|
|
b690d801d4 | ||
|
|
104e4ac5e7 | ||
|
|
e87f4fc44b | ||
|
|
e1f686d850 | ||
|
|
7da7dac3ac | ||
|
|
c2e5afd4e0 | ||
|
|
ad9c386f41 | ||
|
|
a4e3f98b7d | ||
|
|
dd28308c3c | ||
|
|
833c2655ea | ||
|
|
a8295d0b5a | ||
|
|
897e2f6b53 | ||
|
|
5bb77edf45 | ||
|
|
8ddcf4d9c1 | ||
|
|
33ebe2da8e | ||
|
|
b3ee747014 | ||
|
|
fa105b4cae | ||
|
|
483407cdf0 | ||
|
|
008c30fcaa | ||
|
|
3387bb0d83 | ||
|
|
e08e7e4ae6 | ||
|
|
ea1b2c29b9 | ||
|
|
4ad6fbefd3 | ||
|
|
397ecd64d4 | ||
|
|
dd7e8c742c | ||
|
|
2f78132e08 | ||
|
|
0041411c06 | ||
|
|
54955b710c | ||
|
|
1eb5a0d1d3 | ||
|
|
ddda14c52b | ||
|
|
53ddf013df | ||
|
|
5123c9365c | ||
|
|
834e7b1d1c | ||
|
|
6e9a5b026e | ||
|
|
7ad8879b09 | ||
|
|
27cac3ffe6 | ||
|
|
705703a5e2 | ||
|
|
fac31c6628 |
67
CHANGELOG.md
67
CHANGELOG.md
@@ -1,6 +1,73 @@
|
||||
# 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)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **deno:** fix denoify buffer handling ([c2e5afd](https://github.com/windmill-labs/windmill/commit/c2e5afd4e07fb63375832f308da8c744616ee188))
|
||||
|
||||
## [1.69.2](https://github.com/windmill-labs/windmill/compare/v1.69.1...v1.69.2) (2023-02-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **app:** fix all nested behavior ([dd28308](https://github.com/windmill-labs/windmill/commit/dd28308c3cf1877ba3f19dcd2bd20bf1c7896a99))
|
||||
* **frontend:** delete grid item ([008c30f](https://github.com/windmill-labs/windmill/commit/008c30fcaad64af512407f9889a9881fafac0868))
|
||||
* **frontend:** duplicate ([483407c](https://github.com/windmill-labs/windmill/commit/483407cdf0e1ed61de180a904934e950fed4adc3))
|
||||
* **frontend:** Fix findGridItem ([a8295d0](https://github.com/windmill-labs/windmill/commit/a8295d0b5acd08cec42b7939d907df5c25132644))
|
||||
* **frontend:** Fix findGridItem ([5bb77ed](https://github.com/windmill-labs/windmill/commit/5bb77edf45740a75e969b1bef31580271c9d5505))
|
||||
* **frontend:** Fix next id ([8ddcf4d](https://github.com/windmill-labs/windmill/commit/8ddcf4d9c1a8d6dd20ee241a3f308811c49e58f1))
|
||||
* **frontend:** gridtab ([fa105b4](https://github.com/windmill-labs/windmill/commit/fa105b4caeaa2d0e9704a48f6caf8d846839c23e))
|
||||
* **frontend:** rewrote utils ([ea1b2c2](https://github.com/windmill-labs/windmill/commit/ea1b2c29b95282df347ef9c5973917fa3880e843))
|
||||
* **frontend:** wip ([33ebe2d](https://github.com/windmill-labs/windmill/commit/33ebe2da8e81476be62a2567d5012573a8a010b6))
|
||||
|
||||
## [1.69.1](https://github.com/windmill-labs/windmill/compare/v1.69.0...v1.69.1) (2023-02-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **deno:** remove mysql support waiting for deno fix ([dd7e8c7](https://github.com/windmill-labs/windmill/commit/dd7e8c742c83f6a1d13e4343ca626c0b5efc06fb))
|
||||
* **deno:** remove mysql support waiting for deno fix ([2f78132](https://github.com/windmill-labs/windmill/commit/2f78132e081bdf3d7468e022f0e981ebfa52cfb3))
|
||||
* **frontend:** containers and tab fixes v1 ([27cac3f](https://github.com/windmill-labs/windmill/commit/27cac3ffe69c4dac160e9e55ffd1eb8ea348d487))
|
||||
* **frontend:** containers and tab fixes v1 ([705703a](https://github.com/windmill-labs/windmill/commit/705703a5e2f2dc7ceb4c215221f72bf624799841))
|
||||
* **frontend:** containers and tab fixes v1 ([fac31c6](https://github.com/windmill-labs/windmill/commit/fac31c6628b289ad6aae92434e312c4be281a4d2))
|
||||
|
||||
## [1.69.0](https://github.com/windmill-labs/windmill/compare/v1.68.0...v1.69.0) (2023-02-23)
|
||||
|
||||
|
||||
|
||||
104
backend/Cargo.lock
generated
104
backend/Cargo.lock
generated
@@ -255,9 +255,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "axum"
|
||||
version = "0.6.7"
|
||||
version = "0.6.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fb79c228270dcf2426e74864cabc94babb5dbab01a4314e702d2f16540e1591"
|
||||
checksum = "6137c6234afb339e75e764c866e3594900f0211e1315d33779f269bbe2ec6967"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum-core",
|
||||
@@ -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"
|
||||
@@ -553,9 +553,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade"
|
||||
checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09"
|
||||
dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
@@ -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",
|
||||
]
|
||||
|
||||
@@ -987,9 +987,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ena"
|
||||
version = "0.14.0"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3"
|
||||
checksum = "b2e5d13ca2353ab7d0230988629def93914a8c4015f621f9b13ed2955614731d"
|
||||
dependencies = [
|
||||
"log",
|
||||
]
|
||||
@@ -2149,18 +2149,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num_enum"
|
||||
version = "0.5.10"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e0072973714303aa6e3631c7e8e777970cf4bdd25dc4932e41031027b8bcc4e"
|
||||
checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9"
|
||||
dependencies = [
|
||||
"num_enum_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_enum_derive"
|
||||
version = "0.5.10"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0629cbd6b897944899b1f10496d9c4a7ac5878d45fd61bc22e9e79bfbbc29597"
|
||||
checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
@@ -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#c4b67896662b16b5699a779c0e52aa0ca2587fec"
|
||||
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#c4b67896662b16b5699a779c0e52aa0ca2587fec"
|
||||
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#c4b67896662b16b5699a779c0e52aa0ca2587fec"
|
||||
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",
|
||||
@@ -3823,9 +3814,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.108"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d56e159d99e6c2b93995d171050271edb50ecc5288fbc7cc17de8fdce4e58c14"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
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",
|
||||
]
|
||||
@@ -4153,9 +4143,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tower-http"
|
||||
version = "0.3.5"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858"
|
||||
checksum = "5d1d42a9b3f3ec46ba828e8d376aec14592ea199f70a06a548587ecd1c4ab658"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bytes",
|
||||
@@ -4766,7 +4756,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windmill"
|
||||
version = "1.69.0"
|
||||
version = "1.70.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
@@ -4793,7 +4783,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api"
|
||||
version = "1.69.0"
|
||||
version = "1.70.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argon2",
|
||||
@@ -4826,7 +4816,7 @@ dependencies = [
|
||||
"sql-builder",
|
||||
"sqlx",
|
||||
"tempfile",
|
||||
"time 0.3.19",
|
||||
"time 0.3.20",
|
||||
"tokio",
|
||||
"tokio-tar",
|
||||
"tokio-util",
|
||||
@@ -4848,7 +4838,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-client"
|
||||
version = "1.69.0"
|
||||
version = "1.70.1"
|
||||
dependencies = [
|
||||
"base64 0.21.0",
|
||||
"chrono",
|
||||
@@ -4863,7 +4853,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-audit"
|
||||
version = "1.69.0"
|
||||
version = "1.70.1"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"serde",
|
||||
@@ -4876,7 +4866,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-common"
|
||||
version = "1.69.0"
|
||||
version = "1.70.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
@@ -4901,7 +4891,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser"
|
||||
version = "1.69.0"
|
||||
version = "1.70.1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -4909,7 +4899,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-bash"
|
||||
version = "1.69.0"
|
||||
version = "1.70.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools",
|
||||
@@ -4923,7 +4913,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-go"
|
||||
version = "1.69.0"
|
||||
version = "1.70.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools",
|
||||
@@ -4935,7 +4925,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-py"
|
||||
version = "1.69.0"
|
||||
version = "1.70.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools",
|
||||
@@ -4950,7 +4940,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-ts"
|
||||
version = "1.69.0"
|
||||
version = "1.70.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"deno_core",
|
||||
@@ -4964,7 +4954,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-queue"
|
||||
version = "1.69.0"
|
||||
version = "1.70.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@@ -4987,7 +4977,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-worker"
|
||||
version = "1.69.0"
|
||||
version = "1.70.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "windmill"
|
||||
version = "1.69.0"
|
||||
version = "1.70.1"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
@@ -19,7 +19,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.69.0"
|
||||
version = "1.70.1"
|
||||
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
|
||||
edition = "2021"
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
-- Add down migration script here
|
||||
@@ -0,0 +1 @@
|
||||
-- Add down migration script here
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
openapi: "3.0.3"
|
||||
|
||||
info:
|
||||
version: 1.69.0
|
||||
version: 1.70.1
|
||||
title: Windmill API
|
||||
|
||||
contact:
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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)))?;
|
||||
|
||||
@@ -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_execution_sleep_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_sleep_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);
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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())!;
|
||||
|
||||
@@ -13,7 +13,7 @@ import sync from "./sync.ts";
|
||||
import { tryResolveVersion } from "./context.ts";
|
||||
import { GlobalOptions } from "./types.ts";
|
||||
|
||||
const VERSION = "v1.69.0";
|
||||
const VERSION = "v1.70.1";
|
||||
|
||||
let command: any = new Command()
|
||||
.name("wmill")
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -4,8 +4,9 @@ set -e
|
||||
npm ci --ignore-scripts
|
||||
npx --yes openapi-typescript-codegen --input ../backend/windmill-api/openapi.yaml \
|
||||
--output ./src --useOptions \
|
||||
&& sed -i '213 i \\ request.referrerPolicy = \"no-referrer\"\n' src/core/request.ts
|
||||
npx --yes denoify
|
||||
&& sed -i '213 i \\ request.referrerPolicy = \"no-referrer\"\n' src/core/request.ts
|
||||
npx --yes denoify
|
||||
rm -rf windmill-api
|
||||
mv deno_dist windmill-api
|
||||
sed -i 's/buffer DENOIFY: UNKNOWN NODE BUILTIN/node:buffer/' ./windmill-api/core/request.ts
|
||||
rm -rf src/
|
||||
|
||||
@@ -14,7 +14,6 @@ BigInt.prototype.toJSON = function () {
|
||||
};
|
||||
|
||||
export { pgSql, pgClient } from './pg.ts'
|
||||
export { mySql, mysqlClient } from './mysql.ts'
|
||||
|
||||
export type Sql = string
|
||||
export type Email = string
|
||||
|
||||
61
frontend/package-lock.json
generated
61
frontend/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "windmill",
|
||||
"version": "1.69.0",
|
||||
"version": "1.70.1",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "windmill",
|
||||
"version": "1.69.0",
|
||||
"version": "1.70.1",
|
||||
"dependencies": {
|
||||
"@fortawesome/free-brands-svg-icons": "^6.2.1",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.2.1",
|
||||
@@ -14,11 +14,13 @@
|
||||
"@popperjs/core": "^2.11.6",
|
||||
"@redocly/json-to-json-schema": "^0.0.1",
|
||||
"@tanstack/svelte-table": "^8.7.6",
|
||||
"ag-grid-svelte": "^0.1.4",
|
||||
"async-mutex": "^0.4.0",
|
||||
"chart.js": "^3.9.1",
|
||||
"chartjs-adapter-date-fns": "^3.0.0",
|
||||
"chartjs-plugin-zoom": "^2.0.0",
|
||||
"date-fns": "^2.29.3",
|
||||
"fast-equals": "^4.0.3",
|
||||
"highlight.js": "^11.7.0",
|
||||
"lucide-svelte": "^0.115.0",
|
||||
"monaco-editor": "0.33.0",
|
||||
@@ -43,7 +45,7 @@
|
||||
"@types/vscode": "~1.74.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.49.0",
|
||||
"@typescript-eslint/parser": "^5.48.0",
|
||||
"@windmill-labs/svelte-grid": "^5.1.3",
|
||||
"@windmill-labs/svelte-grid": "^5.1.4",
|
||||
"@windmill-labs/svelvet": "^4.0.20",
|
||||
"@zerodevx/svelte-toast": "^0.8.1",
|
||||
"autoprefixer": "^10.4.13",
|
||||
@@ -1263,9 +1265,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@windmill-labs/svelte-grid": {
|
||||
"version": "5.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@windmill-labs/svelte-grid/-/svelte-grid-5.1.3.tgz",
|
||||
"integrity": "sha512-SzfuJ2qYkBYC73x3l+x9l7WcgU+kMlRoFkCgSCOfggi3z5zJU9eX1VYsV1wDjtQVU9OUaAciv1xlUR4OPxrFlg==",
|
||||
"version": "5.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@windmill-labs/svelte-grid/-/svelte-grid-5.1.4.tgz",
|
||||
"integrity": "sha512-alxQ8su/osmHodl1JMta+0dvlc2qnYwiXsoeC8Ys8hrLywD03y5tjxYV0L0WXvOnMKG1S9GbqGuDVCk+UmVuKg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@windmill-labs/svelvet": {
|
||||
@@ -1357,6 +1359,23 @@
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ag-grid-community": {
|
||||
"version": "28.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ag-grid-community/-/ag-grid-community-28.2.1.tgz",
|
||||
"integrity": "sha512-DMZh/xD/FqYP17qJ1M92PolTYe+hrKuEaf+A4h13O6qn2x/xZQrTRGW5DgnQLR/uLMe1XXZQPKR3UKgAlKo69A==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/ag-grid-svelte": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/ag-grid-svelte/-/ag-grid-svelte-0.1.4.tgz",
|
||||
"integrity": "sha512-yKJKZ1o3aE9dgaK4WtmjG+vA9GKqErznvFisDnzi+PPGWXDCSNWuoDGCUpEFJtHKrfooLoUZZHYHi8I9sZ+BCA==",
|
||||
"dependencies": {
|
||||
"svelte": "^3.55.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"ag-grid-community": "^28"
|
||||
}
|
||||
},
|
||||
"node_modules/ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
@@ -2971,6 +2990,11 @@
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fast-equals": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-4.0.3.tgz",
|
||||
"integrity": "sha512-G3BSX9cfKttjr+2o1O22tYMLq0DPluZnYtq1rXumE1SpL/F/SLIfHx08WYQoWSIpeMYf8sRbJ8++71+v6Pnxfg=="
|
||||
},
|
||||
"node_modules/fast-glob": {
|
||||
"version": "3.2.12",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
|
||||
@@ -7904,9 +7928,9 @@
|
||||
}
|
||||
},
|
||||
"@windmill-labs/svelte-grid": {
|
||||
"version": "5.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@windmill-labs/svelte-grid/-/svelte-grid-5.1.3.tgz",
|
||||
"integrity": "sha512-SzfuJ2qYkBYC73x3l+x9l7WcgU+kMlRoFkCgSCOfggi3z5zJU9eX1VYsV1wDjtQVU9OUaAciv1xlUR4OPxrFlg==",
|
||||
"version": "5.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@windmill-labs/svelte-grid/-/svelte-grid-5.1.4.tgz",
|
||||
"integrity": "sha512-alxQ8su/osmHodl1JMta+0dvlc2qnYwiXsoeC8Ys8hrLywD03y5tjxYV0L0WXvOnMKG1S9GbqGuDVCk+UmVuKg==",
|
||||
"dev": true
|
||||
},
|
||||
"@windmill-labs/svelvet": {
|
||||
@@ -7980,6 +8004,20 @@
|
||||
"integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==",
|
||||
"dev": true
|
||||
},
|
||||
"ag-grid-community": {
|
||||
"version": "28.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ag-grid-community/-/ag-grid-community-28.2.1.tgz",
|
||||
"integrity": "sha512-DMZh/xD/FqYP17qJ1M92PolTYe+hrKuEaf+A4h13O6qn2x/xZQrTRGW5DgnQLR/uLMe1XXZQPKR3UKgAlKo69A==",
|
||||
"peer": true
|
||||
},
|
||||
"ag-grid-svelte": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/ag-grid-svelte/-/ag-grid-svelte-0.1.4.tgz",
|
||||
"integrity": "sha512-yKJKZ1o3aE9dgaK4WtmjG+vA9GKqErznvFisDnzi+PPGWXDCSNWuoDGCUpEFJtHKrfooLoUZZHYHi8I9sZ+BCA==",
|
||||
"requires": {
|
||||
"svelte": "^3.55.0"
|
||||
}
|
||||
},
|
||||
"ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
@@ -9151,6 +9189,11 @@
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||
"dev": true
|
||||
},
|
||||
"fast-equals": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-4.0.3.tgz",
|
||||
"integrity": "sha512-G3BSX9cfKttjr+2o1O22tYMLq0DPluZnYtq1rXumE1SpL/F/SLIfHx08WYQoWSIpeMYf8sRbJ8++71+v6Pnxfg=="
|
||||
},
|
||||
"fast-glob": {
|
||||
"version": "3.2.12",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "windmill",
|
||||
"version": "1.69.0",
|
||||
"version": "1.70.1",
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
@@ -25,7 +25,7 @@
|
||||
"@types/vscode": "~1.74.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.49.0",
|
||||
"@typescript-eslint/parser": "^5.48.0",
|
||||
"@windmill-labs/svelte-grid": "^5.1.3",
|
||||
"@windmill-labs/svelte-grid": "^5.1.4",
|
||||
"@windmill-labs/svelvet": "^4.0.20",
|
||||
"@zerodevx/svelte-toast": "^0.8.1",
|
||||
"autoprefixer": "^10.4.13",
|
||||
@@ -66,11 +66,13 @@
|
||||
"@popperjs/core": "^2.11.6",
|
||||
"@redocly/json-to-json-schema": "^0.0.1",
|
||||
"@tanstack/svelte-table": "^8.7.6",
|
||||
"ag-grid-svelte": "^0.1.4",
|
||||
"async-mutex": "^0.4.0",
|
||||
"chart.js": "^3.9.1",
|
||||
"chartjs-adapter-date-fns": "^3.0.0",
|
||||
"chartjs-plugin-zoom": "^2.0.0",
|
||||
"date-fns": "^2.29.3",
|
||||
"fast-equals": "^4.0.3",
|
||||
"highlight.js": "^11.7.0",
|
||||
"lucide-svelte": "^0.115.0",
|
||||
"monaco-editor": "0.33.0",
|
||||
|
||||
4
frontend/src/global.d.ts
vendored
4
frontend/src/global.d.ts
vendored
@@ -44,7 +44,7 @@ declare module '@windmill-labs/svelte-grid' {
|
||||
fastStart?: boolean
|
||||
throttleUpdate?: number
|
||||
throttleResize?: number
|
||||
|
||||
onTopId?: string
|
||||
scroller?: undefined
|
||||
sensor?: number
|
||||
}
|
||||
@@ -60,7 +60,7 @@ declare module '@windmill-labs/svelte-grid' {
|
||||
mount: CustomEvent<>
|
||||
},
|
||||
Slots<T>
|
||||
> {}
|
||||
> { }
|
||||
}
|
||||
|
||||
declare module '@windmill-labs/svelte-grid/build/helper/index.mjs' {
|
||||
|
||||
@@ -396,7 +396,7 @@
|
||||
bind:error={pathError}
|
||||
bind:path
|
||||
initialPath=""
|
||||
namePlaceholder="my_{resource_type}"
|
||||
namePlaceholder="{resource_type}"
|
||||
kind="resource"
|
||||
/>
|
||||
|
||||
@@ -433,7 +433,7 @@
|
||||
{:else}
|
||||
<Path
|
||||
initialPath=""
|
||||
namePlaceholder="my_{resource_type}"
|
||||
namePlaceholder="{resource_type}"
|
||||
bind:error={pathError}
|
||||
bind:path
|
||||
kind="resource"
|
||||
|
||||
@@ -138,8 +138,18 @@
|
||||
|
||||
export function setCode(ncode: string): void {
|
||||
code = ncode
|
||||
if (editor) {
|
||||
editor.setValue(ncode)
|
||||
if (editor?.getModel()) {
|
||||
// editor.setValue(ncode)
|
||||
editor.pushUndoStop()
|
||||
|
||||
editor.executeEdits('set', [
|
||||
{
|
||||
range: editor.getModel()!.getFullModelRange(), // full range
|
||||
text: ncode
|
||||
}
|
||||
])
|
||||
|
||||
editor.pushUndoStop()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
import Tooltip from './Tooltip.svelte'
|
||||
import FolderEditor from './FolderEditor.svelte'
|
||||
import GroupEditor from './GroupEditor.svelte'
|
||||
import { random_adj } from './random_positive_adjetive'
|
||||
|
||||
type PathKind = 'resource' | 'script' | 'variable' | 'flow' | 'schedule' | 'app'
|
||||
let meta: Meta | undefined = undefined
|
||||
@@ -69,15 +70,23 @@
|
||||
|
||||
export async function reset() {
|
||||
if (path == '' || path == 'u//') {
|
||||
meta = { ownerKind: 'user', name: namePlaceholder, owner: '' }
|
||||
meta = {
|
||||
ownerKind: 'user',
|
||||
name: random_adj() + '_' + namePlaceholder,
|
||||
owner: ''
|
||||
}
|
||||
|
||||
meta.owner = $userStore!.username.split('@')[0]
|
||||
|
||||
let i = 1
|
||||
while (await pathExists(metaToPath(meta), kind)) {
|
||||
meta.name = `${namePlaceholder}_${i}`
|
||||
i += 1
|
||||
let newMeta = { ...meta }
|
||||
while (await pathExists(metaToPath(newMeta), kind)) {
|
||||
disabled = true
|
||||
error = 'finding an available name...'
|
||||
newMeta.name = random_adj() + '_' + namePlaceholder
|
||||
}
|
||||
error = ''
|
||||
disabled = false
|
||||
meta = newMeta
|
||||
path = metaToPath(meta)
|
||||
} else {
|
||||
meta = pathToMeta(path)
|
||||
|
||||
@@ -144,7 +144,7 @@
|
||||
disabled={!can_write}
|
||||
bind:path
|
||||
{initialPath}
|
||||
namePlaceholder="my_resource"
|
||||
namePlaceholder="resource"
|
||||
kind="resource"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -180,7 +180,7 @@
|
||||
bind:error={pathError}
|
||||
bind:path
|
||||
{initialPath}
|
||||
namePlaceholder={'my_schedule'}
|
||||
namePlaceholder="schedule"
|
||||
kind="schedule"
|
||||
/>
|
||||
<div class="mb-8" />
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
export let initialPath: string = ''
|
||||
export let template: 'pgsql' | 'mysql' | 'script' = 'script'
|
||||
export let initialArgs: Record<string, any> = {}
|
||||
export let lockedLanguage = false
|
||||
|
||||
const langs: [string, SupportedLanguage][] = [
|
||||
['Typescript', Script.language.DENO],
|
||||
@@ -204,7 +205,7 @@
|
||||
bind:path={script.path}
|
||||
{initialPath}
|
||||
on:enter={() => changeStep(2)}
|
||||
namePlaceholder="my_script"
|
||||
namePlaceholder="script"
|
||||
kind="script"
|
||||
/>
|
||||
<h2 class="border-b pb-1 mt-12 mb-4">Summary</h2>
|
||||
@@ -214,7 +215,13 @@
|
||||
bind:value={script.summary}
|
||||
placeholder="Short summary to be displayed when listed"
|
||||
/>
|
||||
|
||||
<h2 class="border-b pb-1 mt-12 mb-6">Language</h2>
|
||||
{#if lockedLanguage}
|
||||
<div class="text-sm text-gray-600 italic mb-2">
|
||||
As a forked script, the language '{script.language}' cannot be modified.
|
||||
</div>
|
||||
{/if}
|
||||
<div class="flex flex-row gap-2 flex-wrap">
|
||||
{#each langs as [label, lang]}
|
||||
{@const isPicked = script.language == lang && template == 'script'}
|
||||
@@ -228,6 +235,7 @@
|
||||
template = 'script'
|
||||
initContent(lang, script.kind, template)
|
||||
}}
|
||||
disabled={lockedLanguage}
|
||||
>
|
||||
<LanguageIcon {lang} /><span class="ml-2 py-4">{label}</span>
|
||||
</Button>
|
||||
@@ -237,6 +245,7 @@
|
||||
variant="border"
|
||||
color={template == 'pgsql' ? 'blue' : 'dark'}
|
||||
btnClasses={template == 'pgsql' ? '!border-2 !bg-blue-50/75' : 'm-[1px]'}
|
||||
disabled={lockedLanguage}
|
||||
on:click={() => {
|
||||
script.language = Script.language.DENO
|
||||
template = 'pgsql'
|
||||
@@ -245,7 +254,7 @@
|
||||
>
|
||||
<LanguageIcon lang="pgsql" /><span class="ml-2 py-4">PostgreSQL</span>
|
||||
</Button>
|
||||
<Button
|
||||
<!-- <Button
|
||||
size="sm"
|
||||
variant="border"
|
||||
color={template == 'mysql' ? 'blue' : 'dark'}
|
||||
@@ -257,7 +266,7 @@
|
||||
}}
|
||||
>
|
||||
<LanguageIcon lang="mysql" /><span class="ml-2 py-4">MySQL</span>
|
||||
</Button>
|
||||
</Button> -->
|
||||
</div>
|
||||
<div class="mt-16 mb-4">
|
||||
<Button
|
||||
|
||||
@@ -101,6 +101,7 @@
|
||||
|
||||
onMount(() => {
|
||||
inferSchema(code)
|
||||
loadPastTests()
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -148,7 +148,7 @@
|
||||
bind:error={pathError}
|
||||
bind:path
|
||||
{initialPath}
|
||||
namePlaceholder="my_variable"
|
||||
namePlaceholder="variable"
|
||||
kind="variable"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -95,7 +95,6 @@
|
||||
<InputValue {id} input={configuration.triggerOnAppLoad} bind:value={triggerOnAppLoad} />
|
||||
|
||||
<InputValue
|
||||
row={extraQueryParams['row']}
|
||||
{id}
|
||||
input={configuration.disabled}
|
||||
bind:value={disabled}
|
||||
|
||||
@@ -69,7 +69,6 @@
|
||||
<InputValue {id} input={configuration.color} bind:value={color} />
|
||||
<InputValue {id} input={configuration.size} bind:value={size} />
|
||||
<InputValue
|
||||
row={extraQueryParams['row']}
|
||||
{id}
|
||||
input={configuration.disabled}
|
||||
bind:value={disabled}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import type { AppInput } from '../../inputType'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
|
||||
type FitOption = typeof staticValues['objectFitOptions'][number]
|
||||
type FitOption = (typeof staticValues)['objectFitOptions'][number]
|
||||
|
||||
export let id: string
|
||||
export let configuration: Record<string, AppInput>
|
||||
@@ -12,7 +12,7 @@
|
||||
const fit: Record<FitOption, string> = {
|
||||
cover: 'object-cover',
|
||||
contain: 'object-contain',
|
||||
fill: 'object-fill',
|
||||
fill: 'object-fill'
|
||||
}
|
||||
|
||||
let source: string | undefined = undefined
|
||||
@@ -27,8 +27,9 @@
|
||||
<InputValue {id} input={configuration.customStyles} bind:value={customStyles} />
|
||||
|
||||
<img
|
||||
on:pointerdown|preventDefault
|
||||
src={source}
|
||||
alt={altText}
|
||||
style={customStyles}
|
||||
class="w-full h-full {fit[imageFit || 'cover']}"
|
||||
>
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
<script lang="ts">
|
||||
import AgGridSvelte from 'ag-grid-svelte/AgGridSvelte.svelte'
|
||||
import 'ag-grid-community/styles/ag-grid.css'
|
||||
import 'ag-grid-community/styles/ag-theme-alpine.css'
|
||||
|
||||
import { getContext, onMount } from 'svelte'
|
||||
import type { Output } from '../../../rx'
|
||||
import type { AppEditorContext } from '../../../types'
|
||||
import InputValue from '../../helpers/InputValue.svelte'
|
||||
import type { AppInput } from '../../../inputType'
|
||||
import RunnableWrapper from '../../helpers/RunnableWrapper.svelte'
|
||||
import { isObject } from '$lib/utils'
|
||||
|
||||
import Alert from '$lib/components/common/alert/Alert.svelte'
|
||||
|
||||
export let id: string
|
||||
export let componentInput: AppInput | undefined
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let initializing: boolean | undefined = undefined
|
||||
|
||||
export const staticOutputs: string[] = ['selectedRow', 'loading', 'result', 'selectedRowIndex']
|
||||
|
||||
let result: Record<string, any>[] | undefined = undefined
|
||||
|
||||
const {
|
||||
worldStore,
|
||||
staticOutputs: staticOutputsStore,
|
||||
selectedComponent
|
||||
} = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
let selectedRowIndex = -1
|
||||
|
||||
function toggleRow(row: Record<string, any>, rowIndex: number) {
|
||||
if (selectedRowIndex !== rowIndex) {
|
||||
selectedRowIndex = rowIndex
|
||||
outputs?.selectedRow.set(row.original)
|
||||
outputs?.selectedRowIndex.set(rowIndex)
|
||||
}
|
||||
}
|
||||
|
||||
let mounted = false
|
||||
onMount(() => {
|
||||
mounted = true
|
||||
})
|
||||
|
||||
$: selectedRowIndex === -1 &&
|
||||
Array.isArray(result) &&
|
||||
result.length > 0 &&
|
||||
// We need to wait until the component is mounted so the world is created
|
||||
mounted &&
|
||||
outputs &&
|
||||
toggleRow({ original: result[0] }, 0)
|
||||
|
||||
$: outputs = $worldStore?.outputsById[id] as {
|
||||
selectedRowIndex: Output<number>
|
||||
selectedRow: Output<any>
|
||||
result: Output<any>
|
||||
}
|
||||
|
||||
$: outputs?.result?.set(result)
|
||||
|
||||
let clientHeight
|
||||
let clientWidth
|
||||
|
||||
let columnDefs: any = undefined
|
||||
|
||||
let allEditable: boolean | undefined = undefined
|
||||
|
||||
function onCellValueChanged(event) {
|
||||
if (result) {
|
||||
let dataCell = event.newValue
|
||||
try {
|
||||
dataCell = JSON.parse(dataCell)
|
||||
} catch (e) {}
|
||||
result[event.node.rowIndex][event.column.colId] = dataCell
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<InputValue {id} input={configuration.columnDefs} bind:value={columnDefs} />
|
||||
<InputValue {id} input={configuration.allEditable} bind:value={allEditable} />
|
||||
|
||||
<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"
|
||||
bind:clientHeight
|
||||
bind:clientWidth
|
||||
>
|
||||
<div
|
||||
on:pointerdown|stopPropagation={() => {
|
||||
$selectedComponent = id
|
||||
}}
|
||||
style:height="{clientHeight}px"
|
||||
style:width="{clientWidth}px"
|
||||
class="ag-theme-alpine"
|
||||
>
|
||||
<AgGridSvelte
|
||||
bind:rowData={result}
|
||||
{columnDefs}
|
||||
defaultColDef={{ flex: 1, editable: allEditable, onCellValueChanged }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{:else if result != undefined}
|
||||
<Alert title="Parsing issues" type="error" size="xs">
|
||||
The result should be an array of objects
|
||||
</Alert>
|
||||
{/if}
|
||||
</RunnableWrapper>
|
||||
@@ -10,21 +10,17 @@
|
||||
export let input: AppInput
|
||||
export let value: T
|
||||
export let id: string | undefined = undefined
|
||||
export let row: Record<string, any> | undefined = {}
|
||||
export let error: string = ''
|
||||
|
||||
const { worldStore } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
$: state = $worldStore?.state
|
||||
$: input && $worldStore && row && handleConnection()
|
||||
$: input && $worldStore && handleConnection()
|
||||
$: input && input.type == 'template' && $state && (value = getValue(input))
|
||||
$: input && input.type == 'eval' && $state && (value = evalExpr(input))
|
||||
|
||||
function handleConnection() {
|
||||
if (input.type === 'connected') {
|
||||
$worldStore?.connect<any>(input, onValueChange)
|
||||
} else if (input.type === 'row') {
|
||||
setTimeout(() => (value = row?.[input['column']]), 0)
|
||||
} else if (input.type === 'static' || input.type == 'template') {
|
||||
setTimeout(() => (value = getValue(input)), 0)
|
||||
} else if (input.type == 'eval') {
|
||||
@@ -57,7 +53,6 @@
|
||||
Object.fromEntries(Object.entries(value ?? {}).map((x) => [x[0], x[1].peak()]))
|
||||
]
|
||||
})
|
||||
.concat(row ? [['row', row]] : [])
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -239,12 +239,7 @@
|
||||
|
||||
{#each Object.entries(fields ?? {}) as [key, v]}
|
||||
{#if v.type != 'static' && v.type != 'user'}
|
||||
<InputValue
|
||||
{id}
|
||||
input={fields[key]}
|
||||
bind:value={runnableInputValues[key]}
|
||||
row={extraQueryParams['row'] ?? {}}
|
||||
/>
|
||||
<InputValue {id} input={fields[key]} bind:value={runnableInputValues[key]} />
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
|
||||
@@ -23,7 +23,13 @@
|
||||
result: Output<string | undefined>
|
||||
}
|
||||
|
||||
let value: string | undefined = undefined
|
||||
// $: outputs && handleOutputs()
|
||||
|
||||
// function handleOutputs() {
|
||||
// value = outputs.result.peak()
|
||||
// }
|
||||
|
||||
let value: string | undefined = outputs?.result.peak()
|
||||
|
||||
$: items && handleItems()
|
||||
|
||||
@@ -38,9 +44,15 @@
|
||||
}
|
||||
})
|
||||
: []
|
||||
if (items?.[0]?.['value'] != value) {
|
||||
value = JSON.stringify(items?.[0]?.['value'])
|
||||
outputs?.result.set(value)
|
||||
const valuePeak = outputs?.result.peak()
|
||||
if (!valuePeak) {
|
||||
const value0 = items?.[0]?.['value']
|
||||
if (value0 != value && !items.includes(value0)) {
|
||||
value = JSON.stringify(value0)
|
||||
outputs?.result.set(value0)
|
||||
}
|
||||
} else {
|
||||
value = valuePeak
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,6 +86,12 @@
|
||||
$selectedComponent = id
|
||||
}
|
||||
}}
|
||||
on:focus={() => {
|
||||
$selectedComponent = id
|
||||
}}
|
||||
floatingConfig={{
|
||||
strategy: 'fixed'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</AlignWrapper>
|
||||
@@ -82,4 +100,7 @@
|
||||
.app-select .value-container {
|
||||
padding: 0 !important;
|
||||
}
|
||||
.svelte-select-list {
|
||||
z-index: 1000 !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -13,8 +13,7 @@
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
export const staticOutputs: string[] = ['result']
|
||||
|
||||
const { worldStore } = getContext<AppEditorContext>('AppEditorContext')
|
||||
let values: [number] = [0]
|
||||
const { worldStore, selectedComponent } = getContext<AppEditorContext>('AppEditorContext')
|
||||
let min = 0
|
||||
let max = 42
|
||||
let step = 1
|
||||
@@ -22,7 +21,12 @@
|
||||
$: outputs = $worldStore?.outputsById[id] as {
|
||||
result: Output<number | null>
|
||||
}
|
||||
$: if (values) {
|
||||
|
||||
let values: [number] = [outputs?.result.peak() ?? 0]
|
||||
|
||||
$: values && handleValues()
|
||||
|
||||
function handleValues() {
|
||||
// Disallow 'e' character in numbers
|
||||
// if(value && value.toString().includes('e')) {
|
||||
// value = +value.toString().replaceAll('e', '')
|
||||
@@ -40,7 +44,7 @@
|
||||
<AlignWrapper {verticalAlignment}>
|
||||
<div class="flex items-center w-full gap-1 px-1">
|
||||
<span>{min}</span>
|
||||
<div class="grow" on:pointerdown|stopPropagation>
|
||||
<div class="grow" on:pointerdown|stopPropagation={() => ($selectedComponent = id)}>
|
||||
<RangeSlider {step} min={min ?? 0} max={max ?? 1} bind:values />
|
||||
</div>
|
||||
<span>{max}</span>
|
||||
|
||||
@@ -4,19 +4,16 @@
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { AppEditorContext, GridItem } from '../../types'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
|
||||
|
||||
export let id: string
|
||||
export let componentInput: AppInput | undefined
|
||||
export let initializing: boolean | undefined = undefined
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let subGrids: GridItem[][] | undefined = undefined
|
||||
export let componentContainerHeight: number
|
||||
|
||||
export const staticOutputs: string[] = ['loading', 'result']
|
||||
const { focusedGrid, selectedComponent } = getContext<AppEditorContext>('AppEditorContext')
|
||||
let noPadding: boolean | undefined = undefined
|
||||
|
||||
export const staticOutputs: string[] = []
|
||||
const { app, focusedGrid, selectedComponent } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
let result: string[] | undefined = undefined
|
||||
let gridContent: string[] | undefined = undefined
|
||||
|
||||
function onFocus() {
|
||||
@@ -29,15 +26,16 @@
|
||||
$: $selectedComponent === id && onFocus()
|
||||
</script>
|
||||
|
||||
<InputValue {id} input={configuration.noPadding} bind:value={noPadding} />
|
||||
<InputValue {id} input={configuration.gridContent} bind:value={gridContent} />
|
||||
<RunnableWrapper flexWrap bind:componentInput {id} bind:initializing bind:result>
|
||||
{#if subGrids && subGrids[0]}
|
||||
<SubGridEditor
|
||||
bind:subGrid={subGrids[0]}
|
||||
containerHeight={componentContainerHeight}
|
||||
on:focus={() => {
|
||||
$selectedComponent = id
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</RunnableWrapper>
|
||||
|
||||
{#if $app.subgrids?.[`${id}-0`]}
|
||||
<SubGridEditor
|
||||
{noPadding}
|
||||
bind:subGrid={$app.subgrids[`${id}-0`]}
|
||||
containerHeight={componentContainerHeight}
|
||||
on:focus={() => {
|
||||
$selectedComponent = id
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
@@ -6,27 +6,23 @@
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppEditorContext, GridItem } from '../../types'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
|
||||
|
||||
export let id: string
|
||||
export let componentInput: AppInput | undefined
|
||||
export let initializing: boolean | undefined = undefined
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let subGrids: GridItem[][] | undefined = undefined
|
||||
export let componentContainerHeight: number
|
||||
export let tabs: string[]
|
||||
|
||||
export const staticOutputs: string[] = ['loading', 'result', 'selectedTabIndex']
|
||||
const { worldStore, focusedGrid, selectedComponent } =
|
||||
export const staticOutputs: string[] = ['selectedTabIndex']
|
||||
const { app, worldStore, focusedGrid, selectedComponent } =
|
||||
getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
let result: string[] | undefined = undefined
|
||||
let gridContent: string[] | undefined = undefined
|
||||
let selected: string = ''
|
||||
let noPadding: boolean | undefined = undefined
|
||||
|
||||
$: selectedIndex = result?.indexOf(selected) ?? -1
|
||||
$: selectedIndex = tabs?.indexOf(selected) ?? -1
|
||||
|
||||
$: if (result && selected === '') {
|
||||
selected = result[0]
|
||||
$: if ((tabs && selected === '') || !tabs.includes(selected)) {
|
||||
selected = tabs[0]
|
||||
}
|
||||
|
||||
$: outputs = $worldStore?.outputsById[id] as {
|
||||
@@ -38,6 +34,7 @@
|
||||
}
|
||||
|
||||
$: selectedIndex >= 0 && handleTabSelection()
|
||||
|
||||
let tabHeight: number = 0
|
||||
|
||||
function onFocus() {
|
||||
@@ -50,24 +47,30 @@
|
||||
$: $selectedComponent === id && selectedIndex >= 0 && onFocus()
|
||||
</script>
|
||||
|
||||
<InputValue {id} input={configuration.gridContent} bind:value={gridContent} />
|
||||
<RunnableWrapper flexWrap bind:componentInput {id} bind:initializing bind:result>
|
||||
<div bind:clientHeight={tabHeight}>
|
||||
<InputValue {id} input={configuration.tabs} bind:value={tabs} />
|
||||
<InputValue {id} input={configuration.noPadding} bind:value={noPadding} />
|
||||
|
||||
<div>
|
||||
<div bind:clientHeight={tabHeight} on:pointerdown|stopPropagation>
|
||||
<Tabs bind:selected>
|
||||
{#each result ?? [] as res}
|
||||
{#each tabs ?? [] as res}
|
||||
<Tab value={res}>
|
||||
<span class="font-semibold">{res}</span>
|
||||
</Tab>
|
||||
{/each}
|
||||
</Tabs>
|
||||
</div>
|
||||
{#if subGrids && subGrids[selectedIndex]}
|
||||
<SubGridEditor
|
||||
bind:subGrid={subGrids[selectedIndex]}
|
||||
containerHeight={componentContainerHeight - tabHeight}
|
||||
on:focus={() => {
|
||||
$selectedComponent = id
|
||||
}}
|
||||
/>
|
||||
{#if $app.subgrids}
|
||||
{#each tabs ?? [] as res, i}
|
||||
<SubGridEditor
|
||||
visible={i === selectedIndex}
|
||||
bind:subGrid={$app.subgrids[`${id}-${i}`]}
|
||||
{noPadding}
|
||||
containerHeight={componentContainerHeight - tabHeight}
|
||||
on:focus={() => {
|
||||
$selectedComponent = id
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
{/if}
|
||||
</RunnableWrapper>
|
||||
</div>
|
||||
|
||||
@@ -241,7 +241,7 @@
|
||||
bind:error={pathError}
|
||||
bind:path={newPath}
|
||||
initialPath=""
|
||||
namePlaceholder="my_app"
|
||||
namePlaceholder="app"
|
||||
kind="app"
|
||||
/>
|
||||
|
||||
@@ -467,8 +467,8 @@
|
||||
<div>{`Sub grid: ${$focusedGrid.parentComponentId} (${$focusedGrid.subGridIndex})`}</div>
|
||||
<button
|
||||
on:click={() => {
|
||||
$focusedGrid = undefined
|
||||
$selectedComponent = undefined
|
||||
$focusedGrid = undefined
|
||||
}}
|
||||
>
|
||||
<X size={14} />
|
||||
|
||||
@@ -37,13 +37,17 @@
|
||||
const disabledGridItem = fct(gridItem)
|
||||
|
||||
if (disabledGridItem.data.subGrids) {
|
||||
disabledGridItem.data.subGrids = disabledGridItem.data.subGrids.map((subgrid: GridItem[]) =>
|
||||
subgrid.map((subgridItem: GridItem) => fct(subgridItem))
|
||||
disabledGridItem.data.subGrids = disabledGridItem.data.subGrids.map(
|
||||
(subgrid: GridItem[]) => subgrid?.map((subgridItem: GridItem) => fct(subgridItem)) ?? []
|
||||
)
|
||||
}
|
||||
|
||||
return disabledGridItem
|
||||
})
|
||||
|
||||
Object.values($app.subgrids ?? {}).map(
|
||||
(subgrid: GridItem[]) => subgrid?.map((subgridItem: GridItem) => fct(subgridItem)) ?? []
|
||||
)
|
||||
}
|
||||
|
||||
function removeGridElement(component) {
|
||||
@@ -87,6 +91,7 @@
|
||||
function selectComponent(id: string) {
|
||||
if (!$connectingInput.opened) {
|
||||
$selectedComponent = id
|
||||
$focusedGrid = undefined
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,7 +131,6 @@
|
||||
})
|
||||
}
|
||||
})
|
||||
let mounted = false
|
||||
</script>
|
||||
|
||||
<div class="relative w-full z-20 overflow-visible">
|
||||
@@ -151,8 +155,13 @@
|
||||
on:pointerdown={onpointerdown}
|
||||
on:pointerleave={onpointerup}
|
||||
on:pointerup={onpointerup}
|
||||
on:click={() => {
|
||||
$selectedComponent = undefined
|
||||
$focusedGrid = undefined
|
||||
}}
|
||||
>
|
||||
<Grid
|
||||
onTopId={$selectedComponent}
|
||||
fillSpace={false}
|
||||
bind:items={$app.grid}
|
||||
let:dataItem
|
||||
@@ -165,13 +174,11 @@
|
||||
{#if gridComponent.data.id === dataItem.data.id}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
{#if $connectingInput.opened}
|
||||
{#if $connectingInput.opened}
|
||||
<div
|
||||
on:pointerenter={() => ($connectingInput.hoveredComponent = gridComponent.data.id)}
|
||||
on:pointerleave={() => ($connectingInput.hoveredComponent = undefined)}
|
||||
class="absolute w-full h-full bg-black border-2 bg-opacity-25 z-20 flex justify-center items-center"
|
||||
/>
|
||||
{/if}
|
||||
<div
|
||||
on:pointerenter={() => ($connectingInput.hoveredComponent = gridComponent.data.id)}
|
||||
on:pointerleave={() => ($connectingInput.hoveredComponent = undefined)}
|
||||
class="absolute w-full h-full bg-black border-2 bg-opacity-25 z-20 flex justify-center items-center"
|
||||
/>
|
||||
<div
|
||||
style="transform: translate(-50%, -50%);"
|
||||
class="absolute w-fit justify-center bg-indigo-500/90 left-[50%] top-[50%] z-50 px-6 rounded border text-white py-2 text-5xl center-center"
|
||||
@@ -180,6 +187,7 @@
|
||||
{/if}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
on:click|stopPropagation
|
||||
on:pointerdown={() => selectComponent(dataItem.data.id)}
|
||||
class={classNames(
|
||||
'h-full w-full center-center',
|
||||
@@ -189,7 +197,7 @@
|
||||
>
|
||||
<Component
|
||||
{pointerdown}
|
||||
bind:component={gridComponent.data}
|
||||
bind:pComponent={gridComponent.data}
|
||||
selected={$selectedComponent === dataItem.data.id}
|
||||
locked={isFixed(gridComponent)}
|
||||
on:delete={() => removeGridElement(gridComponent.data)}
|
||||
|
||||
@@ -1,36 +1,35 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppEditorContext } from '../types'
|
||||
import { allItems, allItemsWithParent } from '../utils'
|
||||
import PanelSection from './settingsPanel/common/PanelSection.svelte'
|
||||
import ComponentPanel from './settingsPanel/ComponentPanel.svelte'
|
||||
import InputsSpecsEditor from './settingsPanel/InputsSpecsEditor.svelte'
|
||||
import TablePanel from './TablePanel.svelte'
|
||||
|
||||
const { selectedComponent, lazyGrid, app } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { selectedComponent, app } = getContext<AppEditorContext>('AppEditorContext')
|
||||
</script>
|
||||
|
||||
{#each $lazyGrid as gridItem (gridItem.data.id)}
|
||||
{#each $app.grid as gridItem (gridItem.data.id)}
|
||||
{#if gridItem.data.id === $selectedComponent}
|
||||
<ComponentPanel bind:component={gridItem.data} />
|
||||
<ComponentPanel parent={undefined} bind:component={gridItem.data} />
|
||||
{:else if gridItem.data.type === 'tablecomponent'}
|
||||
<TablePanel bind:component={gridItem.data} />
|
||||
{:else if gridItem.data.subGrids}
|
||||
{#each gridItem.data.subGrids as subGrid}
|
||||
{#each subGrid as subGridItem, index (subGridItem.data.id)}
|
||||
{#if subGridItem.data.id === $selectedComponent}
|
||||
<ComponentPanel
|
||||
bind:component={subGridItem.data}
|
||||
onDelete={() => {
|
||||
subGrid.splice(index, 1)
|
||||
subGrid = subGrid
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
{/each}
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
{#if $app.subgrids}
|
||||
{#each Object.keys($app.subgrids ?? {}) as key (key)}
|
||||
{#each $app.subgrids[key] as gridItem (gridItem.data.id)}
|
||||
{#if gridItem.data.id === $selectedComponent}
|
||||
<ComponentPanel parent={key} bind:component={gridItem.data} />
|
||||
{:else if gridItem.data.type === 'tablecomponent'}
|
||||
<TablePanel bind:component={gridItem.data} />
|
||||
{/if}
|
||||
{/each}
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
{#each $app?.hiddenInlineScripts ?? [] as script, index (script.name)}
|
||||
{#if $selectedComponent === `bg_${index}`}
|
||||
<PanelSection title={`Background script inputs`}>
|
||||
|
||||
@@ -5,9 +5,13 @@
|
||||
import { columnConfiguration, isFixed, toggleFixed } from '../gridUtils'
|
||||
import type { AppEditorContext, GridItem } from '../types'
|
||||
import Component from './component/Component.svelte'
|
||||
import { findGridItem } from './appUtils'
|
||||
|
||||
export let subGrid: GridItem[]
|
||||
export let containerHeight: number
|
||||
export let noPadding = false
|
||||
//export let id: string
|
||||
export let subGrid: GridItem[] = []
|
||||
export let visible: boolean = true
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@@ -35,11 +39,17 @@
|
||||
onComponent = id
|
||||
if (!$connectingInput.opened) {
|
||||
$selectedComponent = id
|
||||
/*
|
||||
$focusedGrid = {
|
||||
parentComponentId: parentId,
|
||||
subGridIndex: index
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
@@ -47,33 +57,12 @@
|
||||
|
||||
// @ts-ignore
|
||||
let container
|
||||
|
||||
$: if ($focusedGrid?.parentComponentId !== $selectedComponent) {
|
||||
const gridItemIds = $app.grid
|
||||
.map((gridItem: GridItem) => {
|
||||
if (gridItem.data.id === $focusedGrid?.parentComponentId) {
|
||||
const subGrids = gridItem.data.subGrids ?? []
|
||||
return [
|
||||
gridItem.data.id,
|
||||
...subGrids.map((subGrid: GridItem[]) =>
|
||||
subGrid.map((gridItem: GridItem) => gridItem.data.id)
|
||||
)
|
||||
]
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
})
|
||||
.flat(2)
|
||||
|
||||
if (!gridItemIds.includes($selectedComponent)) {
|
||||
$focusedGrid = undefined
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="relative w-full subgrid " bind:this={container}>
|
||||
<div class="relative w-full subgrid {visible ? 'visible' : 'invisible h-0 '}" bind:this={container}>
|
||||
<div
|
||||
class="px-4 pt-4 overflow-auto {$connectingInput?.opened ? '' : ''}"
|
||||
class:px-2={!noPadding}
|
||||
class="py-2 overflow-auto {$connectingInput?.opened ? '' : ''}"
|
||||
on:pointerdown|stopPropagation={onpointerdown}
|
||||
on:pointerleave={onpointerup}
|
||||
on:pointerup={onpointerup}
|
||||
@@ -121,7 +110,7 @@
|
||||
>
|
||||
<Component
|
||||
{pointerdown}
|
||||
bind:component={gridComponent.data}
|
||||
bind:pComponent={gridComponent.data}
|
||||
selected={$selectedComponent === dataItem.data.id}
|
||||
locked={isFixed(gridComponent)}
|
||||
on:lock={() => lock(gridComponent)}
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
{#each component.actionButtons as actionButton (actionButton.id)}
|
||||
{#if actionButton.id === $selectedComponent}
|
||||
<ComponentPanel
|
||||
parent={undefined}
|
||||
noGrid
|
||||
rowColumns
|
||||
bind:component={actionButton}
|
||||
onDelete={() => {
|
||||
|
||||
173
frontend/src/lib/components/apps/editor/appUtils.ts
Normal file
173
frontend/src/lib/components/apps/editor/appUtils.ts
Normal file
@@ -0,0 +1,173 @@
|
||||
import { getNextId } from '$lib/components/flows/flowStateUtils'
|
||||
import type { App, FocusedGrid, GridItem } from '../types'
|
||||
import { getRecommendedDimensionsByComponent, type AppComponent } from './component'
|
||||
import gridHelp from '@windmill-labs/svelte-grid/src/utils/helper'
|
||||
import { gridColumns } from '../gridUtils'
|
||||
import { allItems } from '../utils'
|
||||
|
||||
function findGridItemById(
|
||||
root: GridItem[],
|
||||
subGrids: Record<string, GridItem[]> | undefined,
|
||||
id: string
|
||||
): GridItem | undefined {
|
||||
for (const gridItem of allItems(root, subGrids)) {
|
||||
if (gridItem.id === id) {
|
||||
return gridItem
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
export function findGridItem(app: App, id: string): GridItem | undefined {
|
||||
return findGridItemById(app.grid, app.subgrids, id)
|
||||
}
|
||||
|
||||
export function getNextGridItemId(app: App): string {
|
||||
const subgridsKeys = allItems(app.grid, app.subgrids).map((x) => x.id)
|
||||
const withoutDash = subgridsKeys.map((element) => element.split('-')[0])
|
||||
const id = getNextId([...new Set(withoutDash)])
|
||||
|
||||
return id
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
export function insertNewGridItem(
|
||||
app: App,
|
||||
data: AppComponent,
|
||||
focusedGrid: FocusedGrid | undefined,
|
||||
keepId?: boolean
|
||||
) {
|
||||
const id = keepId ? data.id : getNextGridItemId(app)
|
||||
|
||||
if (!app.subgrids) {
|
||||
app.subgrids = {}
|
||||
}
|
||||
|
||||
if (!focusedGrid) {
|
||||
const newItem = createNewGridItem(app.grid, id, data)
|
||||
app.grid.push(newItem)
|
||||
} else {
|
||||
const { parentComponentId, subGridIndex } = focusedGrid
|
||||
|
||||
const key = `${parentComponentId}-${subGridIndex ?? 0}`
|
||||
|
||||
const subGrid = app.subgrids[key] ?? []
|
||||
subGrid.push(createNewGridItem(subGrid, id, data))
|
||||
app.subgrids[key] = subGrid
|
||||
}
|
||||
for (let i = 0; i < (data.numberOfSubgrids ?? 0); i++) {
|
||||
app.subgrids[`${id}-${i}`] = []
|
||||
}
|
||||
|
||||
return id
|
||||
}
|
||||
|
||||
export function getAllSubgridsAndComponentIds(
|
||||
app: App,
|
||||
component: AppComponent
|
||||
): [string[], string[]] {
|
||||
const subgrids: string[] = []
|
||||
let components: string[] = [component.id]
|
||||
if (app.subgrids && component.numberOfSubgrids) {
|
||||
for (let i = 0; i < component.numberOfSubgrids; i++) {
|
||||
const key = `${component.id}-${i}`
|
||||
const subgrid = app.subgrids[key]
|
||||
if (subgrid) {
|
||||
subgrids.push(key)
|
||||
for (const item of subgrid) {
|
||||
let [recSubgrids, recComponents] = getAllSubgridsAndComponentIds(app, item.data)
|
||||
subgrids.push(...recSubgrids)
|
||||
components.push(...recComponents)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return [subgrids, components]
|
||||
}
|
||||
|
||||
export function deleteGridItem(
|
||||
app: App,
|
||||
component: AppComponent,
|
||||
parent: string | undefined
|
||||
): string[] {
|
||||
let [subgrids, components] = getAllSubgridsAndComponentIds(app, component)
|
||||
if (app.subgrids) {
|
||||
subgrids.forEach((id) => {
|
||||
delete app.subgrids![id]
|
||||
})
|
||||
}
|
||||
if (!parent) {
|
||||
let index = app.grid.findIndex((x) => x.id == component.id)
|
||||
if (index > -1) {
|
||||
app.grid.splice(index, 1)
|
||||
}
|
||||
} else {
|
||||
let grid = app.subgrids![parent]
|
||||
let index = grid.findIndex((x) => x.id == component.id)
|
||||
if (index > -1) {
|
||||
grid.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
return components
|
||||
}
|
||||
|
||||
export function duplicateGridItem(
|
||||
app: App,
|
||||
parent: string | undefined,
|
||||
id: string
|
||||
): string | undefined {
|
||||
const gridItem = findGridItem(app, id)
|
||||
|
||||
if (gridItem) {
|
||||
const newId = getNextGridItemId(app)
|
||||
const newItem = JSON.parse(JSON.stringify(gridItem))
|
||||
newItem.id = newId
|
||||
newItem.data.id = newId
|
||||
|
||||
let focusedGrid = parent
|
||||
? { parentComponentId: parent.split('-')[0], subGridIndex: Number(parent.split('-')[1]) }
|
||||
: undefined
|
||||
|
||||
return insertNewGridItem(app, newItem.data, focusedGrid)
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
@@ -5,6 +5,8 @@
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import type { AppEditorContext } from '../../types'
|
||||
import ComponentHeader from '../ComponentHeader.svelte'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
|
||||
import {
|
||||
AppBarChart,
|
||||
AppDisplayComponent,
|
||||
@@ -35,8 +37,9 @@
|
||||
AppContainer
|
||||
} from '../../components'
|
||||
import type { AppComponent } from './components'
|
||||
import AppAggridTable from '../../components/display/table/AppAggridTable.svelte'
|
||||
|
||||
export let component: AppComponent
|
||||
export let pComponent: AppComponent
|
||||
export let selected: boolean
|
||||
export let locked: boolean = false
|
||||
export let pointerdown: boolean = false
|
||||
@@ -46,6 +49,11 @@
|
||||
let hover = false
|
||||
let initializing: boolean | undefined = undefined
|
||||
let componentContainerHeight: number = 0
|
||||
|
||||
let component = JSON.parse(JSON.stringify(pComponent))
|
||||
$: if (pComponent && !deepEqual(pComponent, component)) {
|
||||
component = JSON.parse(JSON.stringify(pComponent))
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
@@ -143,6 +151,13 @@
|
||||
bind:componentInput={component.componentInput}
|
||||
bind:actionButtons={component.actionButtons}
|
||||
/>
|
||||
{:else if component.type === 'aggridcomponent'}
|
||||
<AppAggridTable
|
||||
{...component}
|
||||
bind:initializing
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
bind:componentInput={component.componentInput}
|
||||
/>
|
||||
{:else if component.type === 'textcomponent'}
|
||||
<AppText
|
||||
{...component}
|
||||
|
||||
@@ -63,6 +63,7 @@ export type ScatterChartComponent = BaseComponent<'scatterchartcomponent'>
|
||||
export type TableComponent = BaseComponent<'tablecomponent'> & {
|
||||
actionButtons: (BaseAppComponent & ButtonComponent)[]
|
||||
}
|
||||
export type AggridComponent = BaseComponent<'aggridcomponent'>
|
||||
export type DisplayComponent = BaseComponent<'displaycomponent'>
|
||||
export type ImageComponent = BaseComponent<'imagecomponent'>
|
||||
export type InputComponent = BaseComponent<'inputcomponent'>
|
||||
@@ -73,7 +74,9 @@ export type IconComponent = BaseComponent<'iconcomponent'>
|
||||
export type HorizontalDividerComponent = BaseComponent<'horizontaldividercomponent'>
|
||||
export type VerticalDividerComponent = BaseComponent<'verticaldividercomponent'>
|
||||
export type FileInputComponent = BaseComponent<'fileinputcomponent'>
|
||||
export type TabsComponent = BaseComponent<'tabscomponent'>
|
||||
export type TabsComponent = BaseComponent<'tabscomponent'> & {
|
||||
tabs: string[]
|
||||
}
|
||||
export type ContainerComponent = BaseComponent<'containercomponent'>
|
||||
|
||||
export type AppComponent = BaseAppComponent &
|
||||
@@ -108,6 +111,7 @@ export type AppComponent = BaseAppComponent &
|
||||
| VerticalDividerComponent
|
||||
| FileInputComponent
|
||||
| ImageComponent
|
||||
| AggridComponent
|
||||
)
|
||||
|
||||
export type AppComponentDimensions = `${IntRange<
|
||||
@@ -150,7 +154,7 @@ export const components: Record<AppComponent['type'], AppComponentConfig> = {
|
||||
configuration: {},
|
||||
customCss: {
|
||||
header: { class: '', style: '' },
|
||||
container: { class: '', style: '' },
|
||||
container: { class: '', style: '' }
|
||||
} as const,
|
||||
card: false
|
||||
}
|
||||
@@ -161,18 +165,19 @@ export const components: Record<AppComponent['type'], AppComponentConfig> = {
|
||||
dims: '2:8-6:8',
|
||||
data: {
|
||||
softWrap: true,
|
||||
verticalAlignment: 'center',
|
||||
id: '',
|
||||
type: 'containercomponent',
|
||||
configuration: {},
|
||||
componentInput: {
|
||||
type: 'static',
|
||||
fieldType: 'array',
|
||||
subFieldType: 'text',
|
||||
value: ['First Tab', 'Second Tab']
|
||||
configuration: {
|
||||
noPadding: {
|
||||
type: 'static',
|
||||
fieldType: 'boolean',
|
||||
value: false,
|
||||
onlyStatic: true
|
||||
}
|
||||
},
|
||||
componentInput: undefined,
|
||||
card: false,
|
||||
subGrids: [[]]
|
||||
numberOfSubgrids: 1
|
||||
}
|
||||
},
|
||||
textcomponent: {
|
||||
@@ -691,6 +696,48 @@ Hello \${ctx.username}
|
||||
actionButtons: []
|
||||
}
|
||||
},
|
||||
aggridcomponent: {
|
||||
name: 'AgGrid Table',
|
||||
icon: Table2,
|
||||
dims: '3:10-6:10',
|
||||
data: {
|
||||
id: '',
|
||||
type: 'aggridcomponent',
|
||||
configuration: {
|
||||
columnDefs: {
|
||||
type: 'static',
|
||||
fieldType: 'array',
|
||||
subFieldType: 'object',
|
||||
value: [{ field: 'id' }, { field: 'name', editable: true }, { field: 'age' }]
|
||||
},
|
||||
allEditable: {
|
||||
type: 'static',
|
||||
fieldType: 'boolean',
|
||||
value: false,
|
||||
onlyStatic: true
|
||||
}
|
||||
},
|
||||
componentInput: {
|
||||
type: 'static',
|
||||
fieldType: 'array',
|
||||
subFieldType: 'object',
|
||||
value: [
|
||||
{
|
||||
id: 1,
|
||||
name: 'A cell with a long name',
|
||||
age: 42
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'A briefer cell',
|
||||
age: 84
|
||||
}
|
||||
]
|
||||
},
|
||||
customCss: {},
|
||||
card: true,
|
||||
}
|
||||
},
|
||||
checkboxcomponent: {
|
||||
name: 'Toggle',
|
||||
icon: ToggleLeft,
|
||||
@@ -807,19 +854,16 @@ Hello \${ctx.username}
|
||||
type: 'static',
|
||||
value: undefined,
|
||||
fieldType: 'number',
|
||||
onlyStatic: true
|
||||
},
|
||||
max: {
|
||||
type: 'static',
|
||||
value: undefined,
|
||||
fieldType: 'number',
|
||||
onlyStatic: true
|
||||
},
|
||||
step: {
|
||||
type: 'static',
|
||||
value: 1,
|
||||
fieldType: 'number',
|
||||
onlyStatic: true
|
||||
}
|
||||
},
|
||||
customCss: {},
|
||||
@@ -888,19 +932,16 @@ Hello \${ctx.username}
|
||||
type: 'static',
|
||||
value: 42,
|
||||
fieldType: 'number',
|
||||
onlyStatic: true
|
||||
},
|
||||
defaultValue: {
|
||||
type: 'static',
|
||||
value: 20,
|
||||
fieldType: 'number',
|
||||
onlyStatic: true
|
||||
},
|
||||
step: {
|
||||
type: 'static',
|
||||
value: 1,
|
||||
fieldType: 'number',
|
||||
onlyStatic: true
|
||||
}
|
||||
},
|
||||
customCss: {},
|
||||
@@ -922,31 +963,26 @@ Hello \${ctx.username}
|
||||
type: 'static',
|
||||
value: 0,
|
||||
fieldType: 'number',
|
||||
onlyStatic: true
|
||||
},
|
||||
max: {
|
||||
type: 'static',
|
||||
value: 42,
|
||||
fieldType: 'number',
|
||||
onlyStatic: true
|
||||
},
|
||||
defaultLow: {
|
||||
type: 'static',
|
||||
value: 10,
|
||||
fieldType: 'number',
|
||||
onlyStatic: true
|
||||
},
|
||||
defaultHigh: {
|
||||
type: 'static',
|
||||
value: 20,
|
||||
fieldType: 'number',
|
||||
onlyStatic: true
|
||||
},
|
||||
step: {
|
||||
type: 'static',
|
||||
value: 1,
|
||||
fieldType: 'number',
|
||||
onlyStatic: true
|
||||
}
|
||||
},
|
||||
customCss: {},
|
||||
@@ -1012,18 +1048,20 @@ Hello \${ctx.username}
|
||||
dims: '2:8-6:8',
|
||||
data: {
|
||||
softWrap: true,
|
||||
verticalAlignment: 'center',
|
||||
id: '',
|
||||
type: 'tabscomponent',
|
||||
configuration: {},
|
||||
componentInput: {
|
||||
type: 'static',
|
||||
fieldType: 'array',
|
||||
subFieldType: 'text',
|
||||
value: ['First Tab', 'Second Tab']
|
||||
configuration: {
|
||||
noPadding: {
|
||||
type: 'static',
|
||||
fieldType: 'boolean',
|
||||
value: false,
|
||||
onlyStatic: true
|
||||
}
|
||||
},
|
||||
componentInput: undefined,
|
||||
card: false,
|
||||
subGrids: [[], []]
|
||||
numberOfSubgrids: 2,
|
||||
tabs: ['First tab', 'Second tab']
|
||||
}
|
||||
},
|
||||
iconcomponent: {
|
||||
@@ -1195,4 +1233,4 @@ Hello \${ctx.username}
|
||||
card: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ const display: ComponentSet = {
|
||||
'imagecomponent',
|
||||
'htmlcomponent',
|
||||
'tablecomponent',
|
||||
'aggridcomponent',
|
||||
'barchartcomponent',
|
||||
'piechartcomponent',
|
||||
'vegalitecomponent',
|
||||
|
||||
@@ -1,34 +1,20 @@
|
||||
<script lang="ts">
|
||||
import type { AppEditorContext, GridItem } from '../../types'
|
||||
import type { AppEditorContext } from '../../types'
|
||||
import { getContext, onMount } from 'svelte'
|
||||
import { getNextId } from '$lib/components/flows/flowStateUtils'
|
||||
import { isOpenStore } from './store'
|
||||
import { dirtyStore } from '$lib/components/common/confirmationModal/dirtyStore'
|
||||
import { components as componentsRecord, COMPONENT_SETS, type AppComponent } from '../component'
|
||||
import ListItem from './ListItem.svelte'
|
||||
import { insertNewGridItem, createNewGridItem, getNextGridItemId } from '../../utils'
|
||||
import { insertNewGridItem } from '../appUtils'
|
||||
|
||||
const TITLE_PREFIX = 'Component.' as const
|
||||
const { app, selectedComponent, focusedGrid } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
function addComponent(appComponentType: AppComponent['type']): void {
|
||||
// When a new component is added, we need to mark the app as dirty,
|
||||
// so a confirmation modal will appear if the user tries to leave the page
|
||||
$dirtyStore = true
|
||||
|
||||
const grid = $app.grid ?? []
|
||||
const id = getNextGridItemId(grid)
|
||||
|
||||
const data = componentsRecord[appComponentType].data
|
||||
|
||||
if ($focusedGrid) {
|
||||
const { parentComponentId, subGridIndex } = $focusedGrid
|
||||
|
||||
$app.grid = insertNewGridItem($app.grid, parentComponentId, subGridIndex, id, data)
|
||||
} else {
|
||||
const newItem = createNewGridItem(grid, id, data)
|
||||
$app.grid = [...grid, newItem]
|
||||
}
|
||||
const id = insertNewGridItem($app, data, $focusedGrid)
|
||||
|
||||
$selectedComponent = id
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { classNames } from '$lib/utils'
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppEditorContext } from '../../types'
|
||||
import { findGridItem } from '../appUtils'
|
||||
import { components } from '../component'
|
||||
import PanelSection from '../settingsPanel/common/PanelSection.svelte'
|
||||
import ComponentOutputViewer from './ComponentOutputViewer.svelte'
|
||||
@@ -26,7 +27,7 @@
|
||||
}
|
||||
|
||||
function getComponentNameById(componentId: string) {
|
||||
const component = $app.grid.find((c) => c?.data?.id === componentId)
|
||||
const component = findGridItem($app, componentId)
|
||||
|
||||
if (component?.data.type) {
|
||||
return components[component?.data.type].name
|
||||
|
||||
@@ -164,13 +164,13 @@
|
||||
createInlineScriptByLanguage(Script.language.DENO, name, 'pgsql')
|
||||
}}
|
||||
/>
|
||||
<FlowScriptPicker
|
||||
<!-- <FlowScriptPicker
|
||||
label={`MySQL`}
|
||||
lang="mysql"
|
||||
on:click={() => {
|
||||
createInlineScriptByLanguage(Script.language.DENO, name, 'mysql')
|
||||
}}
|
||||
/>
|
||||
/> -->
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
|
||||
@@ -8,10 +8,11 @@
|
||||
import { emptySchema, getScriptByPath } from '$lib/utils'
|
||||
import { faCodeBranch, faExternalLinkAlt, faEye, faPen } from '@fortawesome/free-solid-svg-icons'
|
||||
import type { AppInput, RunnableByPath } from '../../inputType'
|
||||
import { clearResultAppInput, schemaToInputsSpec } from '../../utils'
|
||||
import { clearResultAppInput } from '../../utils'
|
||||
import EmptyInlineScript from './EmptyInlineScript.svelte'
|
||||
import InlineScriptEditor from './InlineScriptEditor.svelte'
|
||||
import { computeFields } from './utils'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
|
||||
export let componentInput: AppInput | undefined
|
||||
export let id: string
|
||||
@@ -43,7 +44,7 @@
|
||||
|
||||
async function refreshScript(x: RunnableByPath) {
|
||||
let { schema } = await getScriptByPath(x.path)
|
||||
if (JSON.stringify(x.schema) != JSON.stringify(schema)) {
|
||||
if (!deepEqual(x.schema, schema)) {
|
||||
x.schema = schema
|
||||
if (componentInput?.type == 'runnable') {
|
||||
componentInput.fields = computeFields(schema, false, componentInput.fields)
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
import SplitPanesWrapper from '$lib/components/splitPanes/SplitPanesWrapper.svelte'
|
||||
import { Pane, Splitpanes } from 'svelte-splitpanes'
|
||||
import InlineScriptsPanelList from './InlineScriptsPanelList.svelte'
|
||||
import InlineScriptEditorPanel from './InlineScriptEditorPanel.svelte'
|
||||
import InlineScriptEditor from './InlineScriptEditor.svelte'
|
||||
import EmptyInlineScript from './EmptyInlineScript.svelte'
|
||||
import InlineScriptEditorPanel from './InlineScriptEditorPanel.svelte'
|
||||
|
||||
const { lazyGrid, app, staticOutputs, runnableComponents } =
|
||||
const { app, staticOutputs, runnableComponents } =
|
||||
getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
let selectedScriptComponentId: string | undefined = undefined
|
||||
@@ -23,16 +23,17 @@
|
||||
{#if !selectedScriptComponentId}
|
||||
<span class="p-2 text-sm text-gray-600">Select a script on the left panel</span>
|
||||
{/if}
|
||||
{#each $lazyGrid as gridComponent, index (index)}
|
||||
{#if gridComponent.data.id === selectedScriptComponentId}
|
||||
|
||||
{#each $app.grid as gridItem (gridItem.data.id)}
|
||||
{#if gridItem.data.id === selectedScriptComponentId}
|
||||
<InlineScriptEditorPanel
|
||||
id={gridComponent.data.id}
|
||||
bind:componentInput={gridComponent.data.componentInput}
|
||||
id={gridItem.data.id}
|
||||
bind:componentInput={gridItem.data.componentInput}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if gridComponent.data.type === 'tablecomponent'}
|
||||
{#each gridComponent.data.actionButtons as actionButton, index (index)}
|
||||
{#if gridItem.data.type === 'tablecomponent'}
|
||||
{#each gridItem.data.actionButtons as actionButton, index (index)}
|
||||
{#if actionButton.id === selectedScriptComponentId}
|
||||
<InlineScriptEditorPanel
|
||||
id={actionButton.id}
|
||||
@@ -42,6 +43,31 @@
|
||||
{/each}
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
{#if $app.subgrids}
|
||||
{#each Object.keys($app.subgrids ?? {}) as key (key)}
|
||||
{#each $app.subgrids[key] as gridItem (gridItem.data.id)}
|
||||
{#if gridItem.data.id === selectedScriptComponentId}
|
||||
<InlineScriptEditorPanel
|
||||
id={gridItem.data.id}
|
||||
bind:componentInput={gridItem.data.componentInput}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if gridItem.data.type === 'tablecomponent'}
|
||||
{#each gridItem.data.actionButtons as actionButton, index (index)}
|
||||
{#if actionButton.id === selectedScriptComponentId}
|
||||
<InlineScriptEditorPanel
|
||||
id={actionButton.id}
|
||||
bind:componentInput={actionButton.componentInput}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
{/if}
|
||||
{/each}
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
{#each $app.unusedInlineScripts as unusedInlineScript, index (index)}
|
||||
{#if `unused-${index}` === selectedScriptComponentId}
|
||||
<InlineScriptEditor
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
$: runnables = getAppScripts($lazyGrid)
|
||||
$: runnables = getAppScripts($lazyGrid, $app.subgrids)
|
||||
|
||||
// When selected component changes, update selectedScriptComponentId
|
||||
$: if ($selectedComponent != selectedScriptComponentId) {
|
||||
|
||||
@@ -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
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import Button from '$lib/components/common/button/Button.svelte'
|
||||
import { faCopy, faTrashAlt } from '@fortawesome/free-solid-svg-icons'
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppEditorContext } from '../../types'
|
||||
import type { AppEditorContext, GridItem } from '../../types'
|
||||
import PanelSection from './common/PanelSection.svelte'
|
||||
import InputsSpecsEditor from './InputsSpecsEditor.svelte'
|
||||
import TableActions from './TableActions.svelte'
|
||||
@@ -10,14 +10,7 @@
|
||||
import ConnectedInputEditor from './inputEditor/ConnectedInputEditor.svelte'
|
||||
import Badge from '$lib/components/common/badge/Badge.svelte'
|
||||
import { capitalize, classNames } from '$lib/utils'
|
||||
import {
|
||||
buildExtraLib,
|
||||
createNewGridItem,
|
||||
fieldTypeToTsType,
|
||||
findParent,
|
||||
getNextGridItemId,
|
||||
insertNewGridItem
|
||||
} from '../../utils'
|
||||
import { buildExtraLib, fieldTypeToTsType } from '../../utils'
|
||||
import Recompute from './Recompute.svelte'
|
||||
import Tooltip from '$lib/components/Tooltip.svelte'
|
||||
import ComponentInputTypeEditor from './ComponentInputTypeEditor.svelte'
|
||||
@@ -27,81 +20,44 @@
|
||||
import type { AppComponent } from '../component'
|
||||
import CssProperty from '../componentsPanel/CssProperty.svelte'
|
||||
import { dirtyStore } from '$lib/components/common/confirmationModal/dirtyStore'
|
||||
import GridTab from './GridTab.svelte'
|
||||
import { duplicateGridItem } from '../appUtils'
|
||||
import { deleteGridItem } from '../appUtils'
|
||||
import MoveToOtherGrid from './MoveToOtherGrid.svelte'
|
||||
|
||||
export let component: AppComponent | undefined
|
||||
export let onDelete: (() => void) | undefined = undefined
|
||||
export let component: AppComponent
|
||||
export let rowColumns = false
|
||||
export let onDelete: (() => void) | undefined = undefined
|
||||
export let parent: string | undefined
|
||||
export let noGrid = false
|
||||
|
||||
const { app, staticOutputs, runnableComponents, selectedComponent, worldStore, focusedGrid } =
|
||||
getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
function duplicateElement(id: string) {
|
||||
const parent = findParent($app.grid, id)
|
||||
|
||||
if (!parent) {
|
||||
return
|
||||
}
|
||||
|
||||
const data: AppComponent = JSON.parse(JSON.stringify(parent.data))
|
||||
$dirtyStore = true
|
||||
|
||||
const grid = $app.grid ?? []
|
||||
const newId = getNextGridItemId(grid)
|
||||
|
||||
if ($focusedGrid) {
|
||||
const { parentComponentId, subGridIndex } = $focusedGrid
|
||||
|
||||
$app.grid = insertNewGridItem($app.grid, parentComponentId, subGridIndex, newId, data)
|
||||
} else {
|
||||
const newItem = createNewGridItem(grid, newId, data)
|
||||
$app.grid = [...grid, newItem]
|
||||
}
|
||||
|
||||
const newId = duplicateGridItem($app, parent, id)
|
||||
$selectedComponent = newId
|
||||
}
|
||||
|
||||
function removeGridElement() {
|
||||
$selectedComponent = undefined
|
||||
if (onDelete && component) {
|
||||
delete $staticOutputs[component.id]
|
||||
$staticOutputs = $staticOutputs
|
||||
|
||||
delete $runnableComponents[component.id]
|
||||
$runnableComponents = $runnableComponents
|
||||
|
||||
onDelete()
|
||||
// Delete static inputs
|
||||
} else {
|
||||
if (component) {
|
||||
$app.grid = $app.grid.filter((gridComponent) => gridComponent.data.id !== component?.id)
|
||||
|
||||
// Delete static inputs
|
||||
delete $staticOutputs[component.id]
|
||||
$staticOutputs = $staticOutputs
|
||||
|
||||
delete $runnableComponents[component.id]
|
||||
$runnableComponents = $runnableComponents
|
||||
$focusedGrid = undefined
|
||||
if (component && !noGrid) {
|
||||
let ids = deleteGridItem($app, component, parent)
|
||||
for (const key of ids) {
|
||||
delete $staticOutputs[key]
|
||||
delete $runnableComponents[key]
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
component &&
|
||||
component.componentInput?.type === 'runnable' &&
|
||||
component.componentInput?.runnable?.type === 'runnableByName'
|
||||
) {
|
||||
const { name, inlineScript } = component.componentInput.runnable
|
||||
delete $staticOutputs[component.id]
|
||||
delete $runnableComponents[component.id]
|
||||
$app = $app
|
||||
$staticOutputs = $staticOutputs
|
||||
$runnableComponents = $runnableComponents
|
||||
|
||||
if (inlineScript) {
|
||||
if (!$app.unusedInlineScripts) {
|
||||
$app.unusedInlineScripts = []
|
||||
}
|
||||
|
||||
$app.unusedInlineScripts.push({
|
||||
name,
|
||||
inlineScript
|
||||
})
|
||||
}
|
||||
}
|
||||
onDelete?.()
|
||||
}
|
||||
|
||||
$: extraLib =
|
||||
@@ -140,18 +96,7 @@
|
||||
|
||||
<div class="flex flex-col w-full gap-2 my-2">
|
||||
{#if component.componentInput.type === 'static'}
|
||||
<StaticInputEditor
|
||||
bind:componentInput={component.componentInput}
|
||||
on:deleteArrayItem={(e) => {
|
||||
if (component?.type === 'tabscomponent') {
|
||||
const deletedIndex = e.detail.index
|
||||
|
||||
if (component.subGrids) {
|
||||
component.subGrids.splice(deletedIndex, 1)
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<StaticInputEditor bind:componentInput={component.componentInput} />
|
||||
{:else if component.componentInput.type === 'template' && component.componentInput !== undefined}
|
||||
<div class="py-1 min-h-[28px] rounded border border-1 border-gray-500">
|
||||
<TemplateEditor fontSize={12} bind:code={component.componentInput.eval} {extraLib} />
|
||||
@@ -204,6 +149,10 @@
|
||||
</PanelSection>
|
||||
{/if}
|
||||
|
||||
{#if component.type === 'tabscomponent'}
|
||||
<GridTab bind:tabs={component.tabs} {component} />
|
||||
{/if}
|
||||
|
||||
{#if component.type === 'tablecomponent' && Array.isArray(component.actionButtons)}
|
||||
<TableActions id={component.id} bind:components={component.actionButtons} />
|
||||
{/if}
|
||||
@@ -228,8 +177,7 @@
|
||||
<PanelSection title="Duplicate">
|
||||
<Button
|
||||
size="xs"
|
||||
color="blue"
|
||||
variant="border"
|
||||
color="dark"
|
||||
startIcon={{ icon: faCopy }}
|
||||
on:click={() => {
|
||||
if (component) {
|
||||
@@ -241,6 +189,10 @@
|
||||
</Button>
|
||||
</PanelSection>
|
||||
|
||||
<PanelSection title="Move to other grid">
|
||||
<MoveToOtherGrid bind:component {parent} />
|
||||
</PanelSection>
|
||||
|
||||
<PanelSection title="Danger zone">
|
||||
<Button
|
||||
size="xs"
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
<script lang="ts">
|
||||
import Button from '$lib/components/common/button/Button.svelte'
|
||||
import { faPlus, faTrashAlt } from '@fortawesome/free-solid-svg-icons'
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppEditorContext } from '../../types'
|
||||
import { deleteGridItem } from '../appUtils'
|
||||
import type { AppComponent } from '../component'
|
||||
import PanelSection from './common/PanelSection.svelte'
|
||||
|
||||
export let tabs: string[]
|
||||
export let component: AppComponent
|
||||
|
||||
const { app, staticOutputs, runnableComponents } =
|
||||
getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
function addTab() {
|
||||
const numberOfTabs = tabs.length
|
||||
tabs = [...tabs, `Tab ${numberOfTabs + 1}`]
|
||||
|
||||
if (!$app.subgrids) {
|
||||
$app.subgrids = {}
|
||||
}
|
||||
|
||||
$app.subgrids[`${component.id}-${numberOfTabs}`] = []
|
||||
component.numberOfSubgrids = tabs.length
|
||||
}
|
||||
|
||||
function deleteSubgrid(index: number) {
|
||||
let subgrid = `${component.id}-${index}`
|
||||
for (const item of $app!.subgrids![subgrid]) {
|
||||
const components = deleteGridItem($app, item.data, subgrid)
|
||||
console.log(components)
|
||||
for (const key in components) {
|
||||
delete $staticOutputs[key]
|
||||
delete $runnableComponents[key]
|
||||
}
|
||||
}
|
||||
$staticOutputs = $staticOutputs
|
||||
$runnableComponents = $runnableComponents
|
||||
for (let i = index; i < tabs.length - 1; i++) {
|
||||
$app!.subgrids![`${component.id}-${i}`] = $app!.subgrids![`${component.id}-${i + 1}`]
|
||||
}
|
||||
tabs.splice(index, 1)
|
||||
tabs = tabs
|
||||
component.numberOfSubgrids = tabs.length
|
||||
$app = $app
|
||||
}
|
||||
</script>
|
||||
|
||||
<PanelSection title={`Tabs ${tabs.length > 0 ? `(${tabs.length})` : ''}`}>
|
||||
{#if tabs.length == 0}
|
||||
<span class="text-xs text-gray-500">No Tabs</span>
|
||||
{/if}
|
||||
<div class="flex gap-2 flex-col mt-2">
|
||||
{#each tabs as value, index (index)}
|
||||
<div class="flex flex-row gap-2 items-center relative">
|
||||
<input type="text" bind:value />
|
||||
|
||||
<div class="absolute top-1 right-1">
|
||||
<Button
|
||||
size="xs"
|
||||
color="light"
|
||||
variant="border"
|
||||
on:click={() => {
|
||||
deleteSubgrid(index)
|
||||
}}
|
||||
iconOnly
|
||||
btnClasses="!text-red-500"
|
||||
startIcon={{ icon: faTrashAlt }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
<Button
|
||||
size="xs"
|
||||
color="light"
|
||||
variant="border"
|
||||
startIcon={{ icon: faPlus }}
|
||||
on:click={addTab}
|
||||
iconOnly
|
||||
/>
|
||||
</div>
|
||||
</PanelSection>
|
||||
@@ -0,0 +1,117 @@
|
||||
<script lang="ts">
|
||||
import { Button } from '$lib/components/common'
|
||||
import { dirtyStore } from '$lib/components/common/confirmationModal/dirtyStore'
|
||||
import { faCopy } from '@fortawesome/free-solid-svg-icons'
|
||||
import { getContext } from 'svelte'
|
||||
import type { App, AppEditorContext } from '../../types'
|
||||
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')
|
||||
|
||||
function listAllSubGrids(app: App) {
|
||||
return app.subgrids ? Object.keys(app.subgrids) : []
|
||||
}
|
||||
|
||||
function findAndDelete(id: string) {
|
||||
const node = findGridItem($app, id)
|
||||
|
||||
if (!node) {
|
||||
return
|
||||
}
|
||||
|
||||
const data: AppComponent = JSON.parse(JSON.stringify(node.data))
|
||||
$dirtyStore = true
|
||||
|
||||
deleteGridItem($app, data, parent)
|
||||
return data
|
||||
}
|
||||
|
||||
function insertComponentInSubGrid(
|
||||
data: AppComponent,
|
||||
targetId: string,
|
||||
targetSubGridIndex: number
|
||||
) {
|
||||
insertNewGridItem(
|
||||
$app,
|
||||
data,
|
||||
{
|
||||
parentComponentId: targetId,
|
||||
subGridIndex: targetSubGridIndex
|
||||
},
|
||||
true
|
||||
)
|
||||
$app.grid = [...$app.grid]
|
||||
}
|
||||
|
||||
function insertComponentInMainGrid(data: AppComponent) {
|
||||
const newComponent = createNewGridItem($app.grid, data.id, data)
|
||||
$app.grid = [...$app.grid, newComponent]
|
||||
}
|
||||
|
||||
function onMove(component: AppComponent) {
|
||||
const data = findAndDelete(component.id)
|
||||
|
||||
if (!data) {
|
||||
return
|
||||
}
|
||||
|
||||
if (selectedOption !== 'main-grid') {
|
||||
const [targetId, targetSubGridIndex] = selectedOption.split('-')
|
||||
insertComponentInSubGrid(data, targetId, parseInt(targetSubGridIndex))
|
||||
} else {
|
||||
insertComponentInMainGrid(data)
|
||||
}
|
||||
}
|
||||
|
||||
const defaultOption = {
|
||||
label: 'Main grid',
|
||||
value: 'main-grid',
|
||||
disabled: parent === undefined
|
||||
}
|
||||
|
||||
$: [subgrids] = component ? getAllSubgridsAndComponentIds($app, component) : [[], []]
|
||||
$: availableGrids = listAllSubGrids($app)
|
||||
$: options = availableGrids
|
||||
? [
|
||||
defaultOption,
|
||||
...availableGrids?.map((grid) => ({
|
||||
label: grid,
|
||||
value: grid,
|
||||
disabled: grid === parent || subgrids.includes(grid)
|
||||
}))
|
||||
]
|
||||
: [defaultOption]
|
||||
</script>
|
||||
|
||||
{#if component && !options.every((option) => option.disabled)}
|
||||
<select bind:value={selectedOption} class="w-full">
|
||||
{#each options as option}
|
||||
<option value={option.value} disabled={option.disabled}>{option.label}</option>
|
||||
{/each}
|
||||
</select>
|
||||
<Button
|
||||
size="xs"
|
||||
color="dark"
|
||||
startIcon={{ icon: faCopy }}
|
||||
on:click={() => {
|
||||
if (component) {
|
||||
onMove(component)
|
||||
}
|
||||
}}
|
||||
>
|
||||
Move component: {component.id}
|
||||
</Button>
|
||||
{:else}
|
||||
<p class="text-gray-500 text-sm">No grids available</p>
|
||||
{/if}
|
||||
@@ -95,17 +95,28 @@
|
||||
}}
|
||||
on:keypress
|
||||
>
|
||||
<Badge color="dark-indigo">
|
||||
{component.id}
|
||||
</Badge>
|
||||
|
||||
<div>
|
||||
<TableActionLabel componentInput={component.configuration.label} />
|
||||
</div>
|
||||
<div>
|
||||
<Button variant="border" color="red" on:click={() => deleteComponent(component.id)}>
|
||||
<Icon class="h-3" data={faTrashAlt} />
|
||||
</Button>
|
||||
</div>
|
||||
<div>
|
||||
<TableActionLabel componentInput={component.configuration.label} />
|
||||
</div>
|
||||
<Badge color="dark-indigo">
|
||||
{component.id}
|
||||
</Badge>
|
||||
</div>
|
||||
{/each}
|
||||
<div class="w-full">
|
||||
<Button
|
||||
btnClasses="w-full"
|
||||
color="light"
|
||||
variant="border"
|
||||
startIcon={{ icon: faPlus }}
|
||||
on:click={addComponent}
|
||||
iconOnly
|
||||
/>
|
||||
</div>
|
||||
</PanelSection>
|
||||
|
||||
@@ -59,7 +59,8 @@ export interface BaseAppComponent extends Partial<Aligned> {
|
||||
* *For example when the component has a popup like `Select`*
|
||||
*/
|
||||
softWrap?: boolean
|
||||
subGrids?: GridItem[][]
|
||||
// Number of subgrids
|
||||
numberOfSubgrids?: number
|
||||
}
|
||||
|
||||
export type ComponentSet = {
|
||||
@@ -99,6 +100,7 @@ export type App = {
|
||||
fields: Record<string, StaticAppInput | ConnectedAppInput | RowAppInput | UserAppInput>
|
||||
}>
|
||||
css?: Record<'viewer' | 'grid' | AppComponent['type'], ComponentCustomCSS>
|
||||
subgrids?: Record<string, GridItem[]>
|
||||
}
|
||||
|
||||
export type ConnectingInput = {
|
||||
@@ -132,7 +134,6 @@ export type AppEditorContext = {
|
||||
}
|
||||
|
||||
export type FocusedGrid = { parentComponentId: string; subGridIndex: number }
|
||||
|
||||
export type EditorMode = 'dnd' | 'preview'
|
||||
export type EditorBreakpoint = 'sm' | 'lg'
|
||||
|
||||
|
||||
@@ -2,19 +2,74 @@ import type { Schema } from '$lib/common'
|
||||
import { FlowService, ScriptService } from '$lib/gen'
|
||||
import { inferArgs } from '$lib/infer'
|
||||
import { emptySchema } from '$lib/utils'
|
||||
import type { AppComponent, AppComponentConfig } from './editor/component'
|
||||
|
||||
import {
|
||||
components as componentsRecord,
|
||||
getRecommendedDimensionsByComponent
|
||||
} from './editor/component'
|
||||
import { gridColumns } from './gridUtils'
|
||||
import gridHelp from '@windmill-labs/svelte-grid/src/utils/helper'
|
||||
import type { AppComponent } from './editor/component'
|
||||
|
||||
import type { AppInput, InputType, ResultAppInput, StaticAppInput } from './inputType'
|
||||
import type { Output } from './rx'
|
||||
import type { App, GridItem } from './types'
|
||||
import { getNextId } from '../flows/flowStateUtils'
|
||||
|
||||
/*
|
||||
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
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
export function allItems(
|
||||
grid: GridItem[],
|
||||
subgrids: Record<string, GridItem[]> | undefined
|
||||
): GridItem[] {
|
||||
if (subgrids == undefined) {
|
||||
return grid
|
||||
}
|
||||
return [...grid, ...Object.values(subgrids).flat()]
|
||||
}
|
||||
|
||||
export function allItemsWithParent(
|
||||
grid: GridItem[],
|
||||
subgrids: Record<string, GridItem[]> | undefined
|
||||
): [GridItem, string | undefined][] {
|
||||
const items: [GridItem, string | undefined][] = grid.map((item) => [item, undefined])
|
||||
if (subgrids == undefined) {
|
||||
return items
|
||||
}
|
||||
return [
|
||||
...items,
|
||||
...Object.entries(subgrids).flatMap(([k, v]) => v.map((g) => [g, k] as [GridItem, string]))
|
||||
]
|
||||
}
|
||||
|
||||
export async function loadSchema(
|
||||
workspace: string,
|
||||
@@ -218,101 +273,3 @@ 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
|
||||
}
|
||||
|
||||
@@ -29,6 +29,11 @@
|
||||
const dispatch = createEventDispatcher()
|
||||
// Order of classes: border, border modifier, bg, bg modifier, text, text modifier, everything else
|
||||
const colorVariants: Record<ButtonType.Color, Record<ButtonType.Variant, string>> = {
|
||||
none: {
|
||||
border: '',
|
||||
contained: ''
|
||||
},
|
||||
|
||||
blue: {
|
||||
border:
|
||||
'border-frost-500 hover:border-frost-700 focus:border-frost-700 bg-white hover:bg-frost-100 focus:bg-frost-100 text-frost-500 hover:text-frost-700 focus:text-frost-700 focus:ring-frost-300',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export const BUTTON_COLORS = ['blue', 'red', 'dark', 'light', 'green', 'gray'] as const
|
||||
export const BUTTON_COLORS = ['blue', 'red', 'dark', 'light', 'green', 'gray', 'none'] as const
|
||||
|
||||
export namespace ButtonType {
|
||||
|
||||
|
||||
@@ -142,12 +142,12 @@
|
||||
dispatch('new', { language: RawScript.language.DENO, kind, subkind: 'pgsql' })}
|
||||
/>
|
||||
|
||||
<FlowScriptPicker
|
||||
<!-- <FlowScriptPicker
|
||||
label={`MySQL`}
|
||||
lang="mysql"
|
||||
on:click={() =>
|
||||
dispatch('new', { language: RawScript.language.DENO, kind, subkind: 'mysql' })}
|
||||
/>
|
||||
/> -->
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
import FlowModuleSuspend from './FlowModuleSuspend.svelte'
|
||||
import FlowRetries from './FlowRetries.svelte'
|
||||
import { getStepPropPicker } from '../previousResults'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
|
||||
import Button from '$lib/components/common/button/Button.svelte'
|
||||
import Alert from '$lib/components/common/alert/Alert.svelte'
|
||||
@@ -87,12 +88,12 @@
|
||||
const { input_transforms, schema } = await loadSchemaFromModule(flowModule)
|
||||
validCode = true
|
||||
setTimeout(() => {
|
||||
if (JSON.stringify(value.input_transforms) !== JSON.stringify(input_transforms)) {
|
||||
if (!deepEqual(value.input_transforms, input_transforms)) {
|
||||
inputTransforms = input_transforms
|
||||
}
|
||||
})
|
||||
|
||||
if (JSON.stringify(schema) !== JSON.stringify($flowStateStore[flowModule.id]?.schema)) {
|
||||
if (!deepEqual(schema, $flowStateStore[flowModule.id]?.schema)) {
|
||||
if (!$flowStateStore[flowModule.id]) {
|
||||
$flowStateStore[flowModule.id] = { schema }
|
||||
} else {
|
||||
|
||||
@@ -42,12 +42,7 @@
|
||||
<svelte:fragment slot="content">
|
||||
<TabContent value="settings-metadata" class="p-4 h-full">
|
||||
<div class="overflow-auto h-full">
|
||||
<Path
|
||||
bind:path={$flowStore.path}
|
||||
{initialPath}
|
||||
namePlaceholder="my_flow"
|
||||
kind="flow"
|
||||
/>
|
||||
<Path bind:path={$flowStore.path} {initialPath} namePlaceholder="flow" kind="flow" />
|
||||
|
||||
<label class="block my-4">
|
||||
<span class="text-gray-700 text-sm">Summary <Required required={false} /></span>
|
||||
|
||||
@@ -234,8 +234,9 @@
|
||||
inline: ''
|
||||
}
|
||||
const wrapperWidth = lang ? 'w-[calc(100%-70px)]' : 'w-[calc(100%-50px)]'
|
||||
let nodeId = id ?? numberToChars(idGenerator.next().value - 1)
|
||||
|
||||
let generatedId = idGenerator.next().value
|
||||
let nodeId = id ?? numberToChars(generatedId - 1)
|
||||
// console.log(id, generatedId, nodeId, charsToNumber(nodeId))
|
||||
return {
|
||||
id: charsToNumber(nodeId),
|
||||
position: { x: -1, y: -1 },
|
||||
@@ -376,6 +377,7 @@
|
||||
}
|
||||
|
||||
function layoutNodes(nodes: Node[]): { nodes: Node[]; height: number } {
|
||||
// console.log('layoutNodes', nodes)
|
||||
const stratify = dagStratify().id(({ id }: Node) => numberToChars(id))
|
||||
const dag = stratify(nodes)
|
||||
|
||||
@@ -401,7 +403,6 @@
|
||||
const edges: Edge[] = []
|
||||
nodes.forEach((node) => {
|
||||
node.parentIds.forEach((pid, i) => {
|
||||
console.log(charsToNumber(pid), pid)
|
||||
// skip virtual nodes such as collect result
|
||||
if (errorHandlers[pid] && node.id < 900 && nodes.find((x) => x.id == errorHandlers[pid])) {
|
||||
edges.push({
|
||||
|
||||
962
frontend/src/lib/components/random_positive_adjetive.ts
Normal file
962
frontend/src/lib/components/random_positive_adjetive.ts
Normal file
@@ -0,0 +1,962 @@
|
||||
const adjectives = ["abundant",
|
||||
"accessible",
|
||||
"accommodative",
|
||||
"accomplished",
|
||||
"accurate",
|
||||
"achievable",
|
||||
"adaptable",
|
||||
"adaptive",
|
||||
"adequate",
|
||||
"adjustable",
|
||||
"admirable",
|
||||
"admiring",
|
||||
"adorable",
|
||||
"adored",
|
||||
"adoring",
|
||||
"adroit",
|
||||
"adulatory",
|
||||
"advanced",
|
||||
"advantageous",
|
||||
"adventuresome",
|
||||
"adventurous",
|
||||
"affable",
|
||||
"affectionate",
|
||||
"affirmative",
|
||||
"affluent",
|
||||
"affordable",
|
||||
"agile",
|
||||
"agreeable",
|
||||
"alluring",
|
||||
"altruistic",
|
||||
"amazed",
|
||||
"amazing",
|
||||
"ambitious",
|
||||
"amenable",
|
||||
"amiable",
|
||||
"amicable",
|
||||
"ample",
|
||||
"amusing",
|
||||
"angelic",
|
||||
"appealing",
|
||||
"appreciable",
|
||||
"appreciated",
|
||||
"appreciative",
|
||||
"appropriate",
|
||||
"ardent",
|
||||
"articulate",
|
||||
"artistic",
|
||||
"assuring",
|
||||
"astonished",
|
||||
"astonishing",
|
||||
"astounded",
|
||||
"astounding",
|
||||
"athletic",
|
||||
"attentive",
|
||||
"attractive",
|
||||
"audible",
|
||||
"auspicious",
|
||||
"authentic",
|
||||
"authoritative",
|
||||
"autonomous",
|
||||
"available",
|
||||
"avid",
|
||||
"awed",
|
||||
"awesome",
|
||||
"awestruck",
|
||||
"balanced",
|
||||
"beauteous",
|
||||
"beautiful",
|
||||
"believable",
|
||||
"beloved",
|
||||
"beneficent",
|
||||
"beneficial",
|
||||
"beneficiary",
|
||||
"benevolent",
|
||||
"best",
|
||||
"best_known",
|
||||
"best_performing",
|
||||
"best_selling",
|
||||
"better",
|
||||
"better_known",
|
||||
"better_than_expected",
|
||||
"blameless",
|
||||
"blissful",
|
||||
"blithe",
|
||||
"bonny",
|
||||
"booming",
|
||||
"boundless",
|
||||
"bountiful",
|
||||
"brainiest",
|
||||
"brainy",
|
||||
"brand_new",
|
||||
"brave",
|
||||
"breathtaking",
|
||||
"bright",
|
||||
"brighter",
|
||||
"brightest",
|
||||
"brilliant",
|
||||
"brisk",
|
||||
"brotherly",
|
||||
"bullish",
|
||||
"buoyant",
|
||||
"calm",
|
||||
"calming",
|
||||
"capable",
|
||||
"captivating",
|
||||
"carefree",
|
||||
"catchy",
|
||||
"celebrated",
|
||||
"celebratory",
|
||||
"champion",
|
||||
"charismatic",
|
||||
"charitable",
|
||||
"charming",
|
||||
"chaste",
|
||||
"cheaper",
|
||||
"cheapest",
|
||||
"cheerful",
|
||||
"cheery",
|
||||
"cherished",
|
||||
"chivalrous",
|
||||
"classic",
|
||||
"classy",
|
||||
"clean",
|
||||
"cleaner",
|
||||
"cleanest",
|
||||
"cleanly",
|
||||
"clear",
|
||||
"clear_cut",
|
||||
"cleared",
|
||||
"clearer",
|
||||
"clever",
|
||||
"coherent",
|
||||
"cohesive",
|
||||
"colorful",
|
||||
"comely",
|
||||
"comfortable",
|
||||
"comforting",
|
||||
"comfy",
|
||||
"commendable",
|
||||
"commodious",
|
||||
"compact",
|
||||
"compassionate",
|
||||
"compatible",
|
||||
"competitive",
|
||||
"complementary",
|
||||
"compliant",
|
||||
"complimentary",
|
||||
"comprehensive",
|
||||
"conciliatory",
|
||||
"concise",
|
||||
"confident",
|
||||
"congenial",
|
||||
"congratulatory",
|
||||
"conscientious",
|
||||
"considerate",
|
||||
"consistent",
|
||||
"constructive",
|
||||
"consummate",
|
||||
"contrasty",
|
||||
"convenient",
|
||||
"convincing",
|
||||
"cool",
|
||||
"coolest",
|
||||
"cooperative",
|
||||
"correct",
|
||||
"cost_effective",
|
||||
"cost_saving",
|
||||
"courageous",
|
||||
"courteous",
|
||||
"courtly",
|
||||
"cozy",
|
||||
"creative",
|
||||
"credible",
|
||||
"crisp",
|
||||
"crisper",
|
||||
"cure_all",
|
||||
"cushy",
|
||||
"cute",
|
||||
"daring",
|
||||
"darling",
|
||||
"dashing",
|
||||
"dauntless",
|
||||
"dazzled",
|
||||
"dazzling",
|
||||
"dead_cheap",
|
||||
"dead_on",
|
||||
"decent",
|
||||
"decisive",
|
||||
"dedicated",
|
||||
"defeated",
|
||||
"deft",
|
||||
"delectable",
|
||||
"delicate",
|
||||
"delicious",
|
||||
"delighted",
|
||||
"delightful",
|
||||
"dependable",
|
||||
"deserving",
|
||||
"desirable",
|
||||
"desirous",
|
||||
"detachable",
|
||||
"devout",
|
||||
"dexterous",
|
||||
"dextrous",
|
||||
"dignified",
|
||||
"diligent",
|
||||
"diplomatic",
|
||||
"dirt_cheap",
|
||||
"distinctive",
|
||||
"distinguished",
|
||||
"diversified",
|
||||
"divine",
|
||||
"dominated",
|
||||
"dumbfounded",
|
||||
"dumbfounding",
|
||||
"dummy_proof",
|
||||
"durable",
|
||||
"dynamic",
|
||||
"eager",
|
||||
"earnest",
|
||||
"eased",
|
||||
"easier",
|
||||
"easiest",
|
||||
"easy",
|
||||
"easy_to_use",
|
||||
"easygoing",
|
||||
"ebullient",
|
||||
"economical",
|
||||
"ecstatic",
|
||||
"educated",
|
||||
"effective",
|
||||
"effectual",
|
||||
"efficacious",
|
||||
"efficient",
|
||||
"effortless",
|
||||
"effusive",
|
||||
"elated",
|
||||
"elegant",
|
||||
"elite",
|
||||
"eloquent",
|
||||
"eminent",
|
||||
"enchanted",
|
||||
"enchanting",
|
||||
"encouraging",
|
||||
"endearing",
|
||||
"endorsed",
|
||||
"energetic",
|
||||
"energy_efficient",
|
||||
"energy_saving",
|
||||
"engaging",
|
||||
"engrossing",
|
||||
"enhanced",
|
||||
"enjoyable",
|
||||
"enough",
|
||||
"enraptured",
|
||||
"enterprising",
|
||||
"entertaining",
|
||||
"enthralled",
|
||||
"enthusiastic",
|
||||
"enticing",
|
||||
"entranced",
|
||||
"entrancing",
|
||||
"enviable",
|
||||
"envious",
|
||||
"equitable",
|
||||
"err_free",
|
||||
"erudite",
|
||||
"ethical",
|
||||
"euphoric",
|
||||
"evaluative",
|
||||
"eventful",
|
||||
"everlasting",
|
||||
"evocative",
|
||||
"exalted",
|
||||
"exalting",
|
||||
"exceeding",
|
||||
"excellent",
|
||||
"exceptional",
|
||||
"excited",
|
||||
"exciting",
|
||||
"exemplary",
|
||||
"exhilarating",
|
||||
"expansive",
|
||||
"exquisite",
|
||||
"extraordinary",
|
||||
"exuberant",
|
||||
"exultant",
|
||||
"eye_catching",
|
||||
"fabulous",
|
||||
"fair",
|
||||
"faithful",
|
||||
"famed",
|
||||
"famous",
|
||||
"fancier",
|
||||
"fancy",
|
||||
"fantastic",
|
||||
"fascinating",
|
||||
"fashionable",
|
||||
"fast",
|
||||
"fast_growing",
|
||||
"fast_paced",
|
||||
"faster",
|
||||
"fastest",
|
||||
"fastest_growing",
|
||||
"faultless",
|
||||
"favorable",
|
||||
"favored",
|
||||
"favorite",
|
||||
"fearless",
|
||||
"feasible",
|
||||
"feature_rich",
|
||||
"feisty",
|
||||
"felicitous",
|
||||
"fertile",
|
||||
"fervent",
|
||||
"fervid",
|
||||
"festive",
|
||||
"fiery",
|
||||
"fine",
|
||||
"fine_looking",
|
||||
"finer",
|
||||
"finest",
|
||||
"firmer",
|
||||
"first_class",
|
||||
"first_in_class",
|
||||
"first_rate",
|
||||
"flashy",
|
||||
"flatter",
|
||||
"flattering",
|
||||
"flawless",
|
||||
"flexible",
|
||||
"flourishing",
|
||||
"fluent",
|
||||
"fond",
|
||||
"foolproof",
|
||||
"foremost",
|
||||
"formidable",
|
||||
"fortuitous",
|
||||
"fortunate",
|
||||
"fragrant",
|
||||
"free",
|
||||
"freed",
|
||||
"fresh",
|
||||
"fresher",
|
||||
"freshest",
|
||||
"friendly",
|
||||
"frugal",
|
||||
"fruitful",
|
||||
"fun",
|
||||
"funny",
|
||||
"futuristic",
|
||||
"gainful",
|
||||
"gallant",
|
||||
"galore",
|
||||
"generous",
|
||||
"genial",
|
||||
"gentle",
|
||||
"gentlest",
|
||||
"genuine",
|
||||
"gifted",
|
||||
"glad",
|
||||
"glamorous",
|
||||
"gleeful",
|
||||
"glimmering",
|
||||
"glistening",
|
||||
"glorious",
|
||||
"glowing",
|
||||
"god_given",
|
||||
"godlike",
|
||||
"gold",
|
||||
"golden",
|
||||
"good",
|
||||
"goodly",
|
||||
"gorgeous",
|
||||
"graceful",
|
||||
"gracious",
|
||||
"grand",
|
||||
"grateful",
|
||||
"gratified",
|
||||
"gratifying",
|
||||
"great",
|
||||
"greatest",
|
||||
"groundbreaking",
|
||||
"guiltless",
|
||||
"gutsy",
|
||||
"halcyon",
|
||||
"hale",
|
||||
"hallowed",
|
||||
"handier",
|
||||
"hands_down",
|
||||
"handsome",
|
||||
"handy",
|
||||
"happier",
|
||||
"happy",
|
||||
"hard_working",
|
||||
"hardier",
|
||||
"hardy",
|
||||
"harmless",
|
||||
"harmonious",
|
||||
"healthful",
|
||||
"healthy",
|
||||
"heartening",
|
||||
"heartfelt",
|
||||
"heartwarming",
|
||||
"heavenly",
|
||||
"helpful",
|
||||
"heroic",
|
||||
"high_quality",
|
||||
"high_spirited",
|
||||
"hilarious",
|
||||
"holy",
|
||||
"honest",
|
||||
"honorable",
|
||||
"honored",
|
||||
"hopeful",
|
||||
"hospitable",
|
||||
"hot",
|
||||
"hottest",
|
||||
"humane",
|
||||
"humble",
|
||||
"humorous",
|
||||
"humourous",
|
||||
"ideal",
|
||||
"idolized",
|
||||
"idyllic",
|
||||
"illuminating",
|
||||
"illustrious",
|
||||
"imaginative",
|
||||
"immaculate",
|
||||
"immense",
|
||||
"impartial",
|
||||
"impassioned",
|
||||
"impeccable",
|
||||
"important",
|
||||
"impressed",
|
||||
"impressive",
|
||||
"improved",
|
||||
"improving",
|
||||
"incredible",
|
||||
"indebted",
|
||||
"individualized",
|
||||
"indulgent",
|
||||
"industrious",
|
||||
"inestimable",
|
||||
"inexpensive",
|
||||
"infallible",
|
||||
"influential",
|
||||
"ingenious",
|
||||
"ingenuous",
|
||||
"innocuous",
|
||||
"innovative",
|
||||
"insightful",
|
||||
"inspirational",
|
||||
"inspiring",
|
||||
"instructive",
|
||||
"instrumental",
|
||||
"integral",
|
||||
"integrated",
|
||||
"intelligent",
|
||||
"intelligible",
|
||||
"interesting",
|
||||
"intimate",
|
||||
"intricate",
|
||||
"intriguing",
|
||||
"intuitive",
|
||||
"invaluable",
|
||||
"inventive",
|
||||
"invigorating",
|
||||
"invincible",
|
||||
"inviolable",
|
||||
"inviolate",
|
||||
"invulnerable",
|
||||
"irreplaceable",
|
||||
"irreproachable",
|
||||
"irresistible",
|
||||
"issue_free",
|
||||
"jaw_dropping",
|
||||
"jolly",
|
||||
"jovial",
|
||||
"joyful",
|
||||
"joyous",
|
||||
"jubilant",
|
||||
"judicious",
|
||||
"keen",
|
||||
"kid_friendly",
|
||||
"kindly",
|
||||
"knowledgeable",
|
||||
"large_capacity",
|
||||
"laudable",
|
||||
"lavish",
|
||||
"law_abiding",
|
||||
"lawful",
|
||||
"leading",
|
||||
"lean",
|
||||
"legendary",
|
||||
"light_hearted",
|
||||
"lighter",
|
||||
"likable",
|
||||
"like",
|
||||
"liked",
|
||||
"lionhearted",
|
||||
"lively",
|
||||
"logical",
|
||||
"long_lasting",
|
||||
"lovable",
|
||||
"loved",
|
||||
"lovely",
|
||||
"loving",
|
||||
"low_cost",
|
||||
"low_price",
|
||||
"low_priced",
|
||||
"low_risk",
|
||||
"lower_priced",
|
||||
"loyal",
|
||||
"lucid",
|
||||
"luckier",
|
||||
"luckiest",
|
||||
"lucky",
|
||||
"lucrative",
|
||||
"luminous",
|
||||
"lush",
|
||||
"lustrous",
|
||||
"luxuriant",
|
||||
"luxurious",
|
||||
"luxury",
|
||||
"lyrical",
|
||||
"magic",
|
||||
"magical",
|
||||
"magnanimous",
|
||||
"magnificent",
|
||||
"majestic",
|
||||
"manageable",
|
||||
"maneuverable",
|
||||
"marvellous",
|
||||
"marvelous",
|
||||
"masterful",
|
||||
"matchless",
|
||||
"mature",
|
||||
"meaningful",
|
||||
"memorable",
|
||||
"merciful",
|
||||
"meritorious",
|
||||
"merry",
|
||||
"mesmerized",
|
||||
"mesmerizing",
|
||||
"meticulous",
|
||||
"mighty",
|
||||
"mind_blowing",
|
||||
"miraculous",
|
||||
"modern",
|
||||
"modest",
|
||||
"momentous",
|
||||
"monumental",
|
||||
"motivated",
|
||||
"multi_purpose",
|
||||
"navigable",
|
||||
"neat",
|
||||
"neatest",
|
||||
"nice",
|
||||
"nicer",
|
||||
"nicest",
|
||||
"nifty",
|
||||
"nimble",
|
||||
"noble",
|
||||
"noiseless",
|
||||
"non_violent",
|
||||
"noteworthy",
|
||||
"nourishing",
|
||||
"observant",
|
||||
"obtainable",
|
||||
"optimal",
|
||||
"optimistic",
|
||||
"opulent",
|
||||
"orderly",
|
||||
"organized",
|
||||
"outstanding",
|
||||
"overjoyed",
|
||||
"pain_free",
|
||||
"painless",
|
||||
"palatial",
|
||||
"pampered",
|
||||
"panoramic",
|
||||
"paramount",
|
||||
"passionate",
|
||||
"patient",
|
||||
"patri",
|
||||
"otic",
|
||||
"peaceable",
|
||||
"peaceful",
|
||||
"peerless",
|
||||
"peppy",
|
||||
"perfect",
|
||||
"permissible",
|
||||
"personalized",
|
||||
"phenomenal",
|
||||
"picturesque",
|
||||
"playful",
|
||||
"pleasant",
|
||||
"pleased",
|
||||
"pleasing",
|
||||
"pleasurable",
|
||||
"plentiful",
|
||||
"plush",
|
||||
"poetic",
|
||||
"poignant",
|
||||
"poised",
|
||||
"polished",
|
||||
"polite",
|
||||
"popular",
|
||||
"portable",
|
||||
"posh",
|
||||
"positive",
|
||||
"powerful",
|
||||
"praiseworthy",
|
||||
"praising",
|
||||
"pre_eminent",
|
||||
"precious",
|
||||
"precise",
|
||||
"preeminent",
|
||||
"preferable",
|
||||
"premier",
|
||||
"prestigious",
|
||||
"pretty",
|
||||
"priceless",
|
||||
"principled",
|
||||
"privileged",
|
||||
"prize",
|
||||
"proactive",
|
||||
"problem_free",
|
||||
"prodigious",
|
||||
"productive",
|
||||
"proficient",
|
||||
"profound",
|
||||
"profuse",
|
||||
"progressive",
|
||||
"prolific",
|
||||
"prominent",
|
||||
"promised",
|
||||
"promising",
|
||||
"prompt",
|
||||
"proper",
|
||||
"propitious",
|
||||
"prosperous",
|
||||
"protective",
|
||||
"proud",
|
||||
"proven",
|
||||
"prudent",
|
||||
"punctual",
|
||||
"pure",
|
||||
"purposeful",
|
||||
"quaint",
|
||||
"qualified",
|
||||
"quicker",
|
||||
"quiet",
|
||||
"quieter",
|
||||
"radiant",
|
||||
"rapid",
|
||||
"rapt",
|
||||
"rapturous",
|
||||
"rational",
|
||||
"razor_sharp",
|
||||
"reachable",
|
||||
"readable",
|
||||
"ready",
|
||||
"realistic",
|
||||
"realizable",
|
||||
"reasonable",
|
||||
"reasoned",
|
||||
"receptive",
|
||||
"recommended",
|
||||
"record_setting",
|
||||
"redeeming",
|
||||
"refined",
|
||||
"reformed",
|
||||
"refreshed",
|
||||
"refreshing",
|
||||
"regal",
|
||||
"rejoicing",
|
||||
"relaxed",
|
||||
"reliable",
|
||||
"remarkable",
|
||||
"renewed",
|
||||
"renowned",
|
||||
"replaceable",
|
||||
"reputable",
|
||||
"resilient",
|
||||
"resolute",
|
||||
"resounding",
|
||||
"resourceful",
|
||||
"respectable",
|
||||
"respectful",
|
||||
"resplendent",
|
||||
"responsive",
|
||||
"restful",
|
||||
"restored",
|
||||
"retractable",
|
||||
"reverent",
|
||||
"revolutionary",
|
||||
"rewarding",
|
||||
"rich",
|
||||
"richer",
|
||||
"right",
|
||||
"righteous",
|
||||
"rightful",
|
||||
"risk_free",
|
||||
"robust",
|
||||
"rock_star",
|
||||
"romantic",
|
||||
"roomier",
|
||||
"roomy",
|
||||
"rosy",
|
||||
"safe",
|
||||
"saintly",
|
||||
"salutary",
|
||||
"sane",
|
||||
"satisfactory",
|
||||
"satisfied",
|
||||
"satisfying",
|
||||
"scenic",
|
||||
"seamless",
|
||||
"seasoned",
|
||||
"secure",
|
||||
"selective",
|
||||
"self_determination",
|
||||
"self_respect",
|
||||
"self_satisfaction",
|
||||
"self_sufficiency",
|
||||
"self_sufficient",
|
||||
"sensational",
|
||||
"sensible",
|
||||
"sensitive",
|
||||
"serene",
|
||||
"sexy",
|
||||
"sharp",
|
||||
"sharper",
|
||||
"sharpest",
|
||||
"shiny",
|
||||
"significant",
|
||||
"silent",
|
||||
"simpler",
|
||||
"simplest",
|
||||
"simplified",
|
||||
"sincere",
|
||||
"skilled",
|
||||
"skillful",
|
||||
"sleek",
|
||||
"slick",
|
||||
"smart",
|
||||
"smarter",
|
||||
"smartest",
|
||||
"smiling",
|
||||
"smitten",
|
||||
"smooth",
|
||||
"smoother",
|
||||
"smoothest",
|
||||
"snappy",
|
||||
"snazzy",
|
||||
"sociable",
|
||||
"soft",
|
||||
"softer",
|
||||
"solicitous",
|
||||
"solid",
|
||||
"sophisticated",
|
||||
"soulful",
|
||||
"spacious",
|
||||
"sparkling",
|
||||
"spectacular",
|
||||
"speedy",
|
||||
"spellbinding",
|
||||
"spellbound",
|
||||
"spirited",
|
||||
"spiritual",
|
||||
"splendid",
|
||||
"spontaneous",
|
||||
"sporty",
|
||||
"spotless",
|
||||
"sprightly",
|
||||
"stable",
|
||||
"stainless",
|
||||
"state_of_the_art",
|
||||
"stately",
|
||||
"statuesque",
|
||||
"staunch",
|
||||
"steadfast",
|
||||
"steadiest",
|
||||
"steady",
|
||||
"stellar",
|
||||
"stimulating",
|
||||
"stimulative",
|
||||
"straightforward",
|
||||
"streamlined",
|
||||
"striking",
|
||||
"strong",
|
||||
"stronger",
|
||||
"strongest",
|
||||
"stunned",
|
||||
"stunning",
|
||||
"stupendous",
|
||||
"sturdier",
|
||||
"sturdy",
|
||||
"stylish",
|
||||
"stylized",
|
||||
"suave",
|
||||
"sublime",
|
||||
"subsidized",
|
||||
"substantive",
|
||||
"succeeding",
|
||||
"successful",
|
||||
"sufficient",
|
||||
"suitable",
|
||||
"sumptuous",
|
||||
"super",
|
||||
"superb",
|
||||
"superior",
|
||||
"supple",
|
||||
"supported",
|
||||
"supporting",
|
||||
"supportive",
|
||||
"supreme",
|
||||
"surreal",
|
||||
"sustainable",
|
||||
"swank",
|
||||
"swankier",
|
||||
"swankiest",
|
||||
"swanky",
|
||||
"sweeping",
|
||||
"sweet",
|
||||
"sweetheart",
|
||||
"swift",
|
||||
"talented",
|
||||
"tantalizing",
|
||||
"tempting",
|
||||
"tenacious",
|
||||
"tender",
|
||||
"terrific",
|
||||
"thankful",
|
||||
"thinner",
|
||||
"thoughtful",
|
||||
"thrifty",
|
||||
"thrilled",
|
||||
"thrilling",
|
||||
"thriving",
|
||||
"thumb_up",
|
||||
"thumbs_up",
|
||||
"tidy",
|
||||
"time_honored",
|
||||
"timely",
|
||||
"titillating",
|
||||
"tolerable",
|
||||
"toll_free",
|
||||
"top",
|
||||
"top_notch",
|
||||
"top_quality",
|
||||
"topnotch",
|
||||
"tops",
|
||||
"tough",
|
||||
"tougher",
|
||||
"toughest",
|
||||
"tranquil",
|
||||
"transparent",
|
||||
"trendy",
|
||||
"triumphal",
|
||||
"triumphant",
|
||||
"trouble_free",
|
||||
"trusted",
|
||||
"trusting",
|
||||
"trustworthy",
|
||||
"trusty",
|
||||
"truthful",
|
||||
"twinkly",
|
||||
"ultra_crisp",
|
||||
"unabashed",
|
||||
"unaffected",
|
||||
"unassailable",
|
||||
"unbeatable",
|
||||
"unbiased",
|
||||
"unbound",
|
||||
"uncomplicated",
|
||||
"unconditional",
|
||||
"undamaged",
|
||||
"undaunted",
|
||||
"understandable",
|
||||
"undisputable",
|
||||
"undisputed",
|
||||
"unencumbered",
|
||||
"unequivocal",
|
||||
"unfettered",
|
||||
"unforgettable",
|
||||
"unlimited",
|
||||
"unmatched",
|
||||
"unparalleled",
|
||||
"unquestionable",
|
||||
"unreal",
|
||||
"unrestricted",
|
||||
"unrivaled",
|
||||
"unselfish",
|
||||
"unwavering",
|
||||
"upbeat",
|
||||
"upscale",
|
||||
"usable",
|
||||
"useable",
|
||||
"useful",
|
||||
"user_friendly",
|
||||
"user_replaceable",
|
||||
"valiant",
|
||||
"valuable",
|
||||
"verifiable",
|
||||
"veritable",
|
||||
"versatile",
|
||||
"vibrant",
|
||||
"victorious",
|
||||
"viewable",
|
||||
"vigilant",
|
||||
"virtuous",
|
||||
"visionary",
|
||||
"vivacious",
|
||||
"vivid",
|
||||
"warm",
|
||||
"warmer",
|
||||
"warmhearted",
|
||||
"wealthy",
|
||||
"welcome",
|
||||
"well",
|
||||
"well_backlit",
|
||||
"well_balanced",
|
||||
"well_behaved",
|
||||
"well_being",
|
||||
"well_bred",
|
||||
"well_connected",
|
||||
"well_educated",
|
||||
"well_established",
|
||||
"well_informed",
|
||||
"well_intentioned",
|
||||
"well_known",
|
||||
"well_made",
|
||||
"well_managed",
|
||||
"well_mannered",
|
||||
"well_positioned",
|
||||
"well_received",
|
||||
"well_regarded",
|
||||
"well_rounded",
|
||||
"well_run",
|
||||
"well_wishers",
|
||||
"wholesome",
|
||||
"wieldy",
|
||||
"willing",
|
||||
"winning",
|
||||
"wise",
|
||||
"witty",
|
||||
"won",
|
||||
"wonderful",
|
||||
"wondrous",
|
||||
"workable",
|
||||
"world_famous",
|
||||
"worth",
|
||||
"worthwhile",
|
||||
"worthy",
|
||||
"young",
|
||||
"youthful",
|
||||
"zippy"]
|
||||
|
||||
export function random_adj() { return adjectives[Math.floor(Math.random() * adjectives.length)] }
|
||||
@@ -98,7 +98,7 @@ export async function main(
|
||||
export const MYSQL_INIT_CODE = `import {
|
||||
mySql,
|
||||
type Resource
|
||||
} from "https://deno.land/x/windmill@v${__pkg__.version}/mod.ts";
|
||||
} from "https://deno.land/x/windmill@v${__pkg__.version}/mysql.ts";
|
||||
|
||||
// MySQL parameterized statement. No SQL injection is possible.
|
||||
export async function main(
|
||||
|
||||
@@ -11,8 +11,6 @@
|
||||
// Default
|
||||
let schema: Schema = emptySchema()
|
||||
|
||||
let nodraft = $page.url.searchParams.get('nodraft')
|
||||
|
||||
const templatePath = $page.url.searchParams.get('template')
|
||||
const hubPath = $page.url.searchParams.get('hub')
|
||||
|
||||
@@ -73,4 +71,4 @@
|
||||
$dirtyStore = true
|
||||
</script>
|
||||
|
||||
<ScriptBuilder {script} />
|
||||
<ScriptBuilder lockedLanguage={templatePath != null || hubPath != null} {script} />
|
||||
|
||||
@@ -4,8 +4,8 @@ verify_ssl = true
|
||||
name = "pypi"
|
||||
|
||||
[packages]
|
||||
wmill = ">=1.69.0"
|
||||
wmill_pg = ">=1.69.0"
|
||||
wmill = ">=1.70.1"
|
||||
wmill_pg = ">=1.70.1"
|
||||
sendgrid = "*"
|
||||
mysql-connector-python = "*"
|
||||
pymongo = "*"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: "3.0.3"
|
||||
|
||||
info:
|
||||
version: 1.69.0
|
||||
version: 1.70.1
|
||||
title: OpenFlow Spec
|
||||
contact:
|
||||
name: Ruben Fiszel
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "wmill"
|
||||
version = "1.69.0"
|
||||
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.0"
|
||||
windmill-api = "^1.70.1"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry>=1.0.2", "poetry-dynamic-versioning"]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "wmill-pg"
|
||||
version = "1.69.0"
|
||||
version = "1.70.1"
|
||||
description = "An extension client for the wmill client library focused on pg"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://windmill.dev"
|
||||
|
||||
@@ -1 +1 @@
|
||||
1.69.0
|
||||
1.70.1
|
||||
|
||||
Reference in New Issue
Block a user