Merge branch 'main' into fr/xyflow

This commit is contained in:
Faton Ramadani
2024-08-12 13:06:46 +02:00
48 changed files with 795 additions and 477 deletions
+24 -28
View File
@@ -1,6 +1,7 @@
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
IMAGE_NAME: ${{ github.event_name != 'pull_request' && github.repository || 'windmill-labs/windmill-test' }}
DEV_SHA: ${{ github.event_name != 'pull_request' && 'dev' || format('pr-{0}', github.event.number) }}
name: Build windmill:main
on:
@@ -62,7 +63,6 @@ jobs:
flavor: |
latest=false
tags: |
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
@@ -75,7 +75,7 @@ jobs:
build-args: |
features=embedding,parquet,openidconnect,jemalloc
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:dev
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.DEV_SHA }}
${{ steps.meta-public.outputs.tags }}
labels: |
${{ steps.meta-public.outputs.labels }}
@@ -113,7 +113,6 @@ jobs:
flavor: |
latest=false
tags: |
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
@@ -137,7 +136,7 @@ jobs:
build-args: |
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:dev
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:${{ env.DEV_SHA }}
${{ steps.meta-ee-public.outputs.tags }}
labels: |
${{ steps.meta-ee-public.outputs.labels }}
@@ -176,7 +175,6 @@ jobs:
flavor: |
latest=false
tags: |
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
@@ -250,25 +248,25 @@ jobs:
- run: |
# pulling docker image with desired arch so that actions-docker-extract doesn't do it
docker pull --platform "linux/$ARCH" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:dev
docker pull --platform "linux/$ARCH" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:dev
docker pull --platform "linux/$ARCH" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.DEV_SHA }}
docker pull --platform "linux/$ARCH" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:${{ env.DEV_SHA }}
- run: |
# Checks the image is in docker prior to running actions-docker-extract. It fails if not
# Also useful to visually check that the arch is the right opencontainers
docker image inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:dev
docker image inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:dev
docker image inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.DEV_SHA }}
docker image inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:${{ env.DEV_SHA }}
- uses: shrink/actions-docker-extract@v3
id: extract
with:
image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:dev
image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.DEV_SHA }}
path: "/usr/src/app/windmill"
- uses: shrink/actions-docker-extract@v3
id: extract-ee
with:
image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:dev
image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:${{ env.DEV_SHA }}
path: "/usr/src/app/windmill"
- name: Rename binary with corresponding architecture
@@ -294,25 +292,25 @@ jobs:
# - run: |
# # pulling docker image with desired arch so that actions-docker-extract doesn't do it
# docker pull --platform "linux/$ARCH" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:dev
# docker pull --platform "linux/$ARCH" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:dev
# docker pull --platform "linux/$ARCH" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.DEV_SHA }}
# docker pull --platform "linux/$ARCH" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:${{ env.DEV_SHA }}
# - run: |
# # Checks the image is in docker prior to running actions-docker-extract. It fails if not
# # Also useful to visually check that the arch is the right opencontainers
# docker image inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:dev
# docker image inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:dev
# docker image inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.DEV_SHA }}
# docker image inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:${{ env.DEV_SHA }}
# - uses: shrink/actions-docker-extract@v3
# id: extract
# with:
# image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:dev
# image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.DEV_SHA }}
# path: "/usr/src/app/windmill"
# - uses: shrink/actions-docker-extract@v3
# id: extract-ee
# with:
# image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:dev
# image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:${{ env.DEV_SHA }}
# path: "/usr/src/app/windmill"
# - name: Rename binary with corresponding architecture
@@ -354,6 +352,7 @@ jobs:
tag_latest:
runs-on: ubicloud
needs: [run_integration_test, build]
if: github.event_name != 'pull_request'
steps:
- uses: actions/checkout@v4
with:
@@ -366,12 +365,13 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
- name: Tag main and latest
run: |
docker buildx imagetools create ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:dev --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
docker buildx imagetools create ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:dev --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:main
docker buildx imagetools create ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.DEV_SHA }} --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
docker buildx imagetools create ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.DEV_SHA }} --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:main
tag_latest_ee:
runs-on: ubicloud
needs: [run_integration_test, build_ee]
if: github.event_name != 'pull_request'
steps:
- uses: actions/checkout@v4
with:
@@ -384,8 +384,8 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
- name: Tag main and latest for ee
run: |
docker buildx imagetools create ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:dev --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:latest
docker buildx imagetools create ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:dev --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:main
docker buildx imagetools create ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:${{ env.DEV_SHA }} --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:latest
docker buildx imagetools create ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:${{ env.DEV_SHA }} --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:main
verify_ee_image_vulnerabilities:
runs-on: ubicloud
@@ -432,6 +432,7 @@ jobs:
build_ee_nsjail:
needs: [build_ee]
runs-on: ubicloud
if: github.event_name != 'pull_request'
steps:
- uses: actions/checkout@v4
with:
@@ -451,8 +452,6 @@ jobs:
flavor: |
latest=false
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha,enable=true,priority=100,prefix=,suffix=,format=short
@@ -480,6 +479,7 @@ jobs:
build_ee_reports_privately:
needs: [build_ee_nsjail]
runs-on: ubicloud
if: github.event_name != 'pull_request'
steps:
- uses: actions/checkout@v4
with:
@@ -503,8 +503,6 @@ jobs:
images: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee-reports
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha,enable=true,priority=100,prefix=,suffix=,format=short
@@ -589,7 +587,6 @@ jobs:
flavor: |
latest=false
tags: |
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
@@ -636,7 +633,6 @@ jobs:
flavor: |
latest=false
tags: |
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
+18
View File
@@ -1,5 +1,23 @@
# Changelog
## [1.378.0](https://github.com/windmill-labs/windmill/compare/v1.377.1...v1.378.0) (2024-08-12)
### Features
* **cli:** add customerBundler support ([70d1e6c](https://github.com/windmill-labs/windmill/commit/70d1e6c7208b59890ad4d15cad28b1f32d7970d2))
* windmill embed ([d56a956](https://github.com/windmill-labs/windmill/commit/d56a956b9a471cca4042f43ba4e3922e42f121f1))
### Bug Fixes
* allow user resources in app to work within iframes ([6272c9f](https://github.com/windmill-labs/windmill/commit/6272c9ff41e5359f38ae13e879aa615da0fb0d09))
* **frontend:** improve default id of the components of the topbar ([#4222](https://github.com/windmill-labs/windmill/issues/4222)) ([7115a35](https://github.com/windmill-labs/windmill/commit/7115a3577947c52aa72edcc134dcb8e739208df0))
* **frontend:** support plus sign in emails format fields [#4223](https://github.com/windmill-labs/windmill/issues/4223) ([c0853ea](https://github.com/windmill-labs/windmill/commit/c0853eafc9f9bf6d454c6a70800c98c75c9d8ba3))
* **frontend:** user resource picker for app use lightweight component ([c03c0f6](https://github.com/windmill-labs/windmill/commit/c03c0f61726e7b6196fe5222c44d4d635e907b20))
* improve default value handling for date & date-time in apps ([e779f96](https://github.com/windmill-labs/windmill/commit/e779f963869fcb22d73f02b6a806e3fdac978959))
* improve license key ui ([#4220](https://github.com/windmill-labs/windmill/issues/4220)) ([0655803](https://github.com/windmill-labs/windmill/commit/06558038601d6f78b234519d4b48f229a2bf9fc8))
## [1.377.1](https://github.com/windmill-labs/windmill/compare/v1.377.0...v1.377.1) (2024-08-08)
+134 -133
View File
@@ -459,7 +459,7 @@ dependencies = [
"proc-macro2",
"quote",
"swc_macros_common",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -542,7 +542,7 @@ checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c"
dependencies = [
"async-channel 2.3.1",
"async-executor",
"async-io 2.3.3",
"async-io 2.3.4",
"async-lock 3.4.0",
"blocking",
"futures-lite 2.3.0",
@@ -571,9 +571,9 @@ dependencies = [
[[package]]
name = "async-io"
version = "2.3.3"
version = "2.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d6baa8f0178795da0e71bc42c9e5d13261aac7ee549853162e66a241ba17964"
checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8"
dependencies = [
"async-lock 3.4.0",
"cfg-if",
@@ -581,11 +581,11 @@ dependencies = [
"futures-io",
"futures-lite 2.3.0",
"parking",
"polling 3.7.2",
"polling 3.7.3",
"rustix 0.38.34",
"slab",
"tracing",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -635,7 +635,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -683,7 +683,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -725,7 +725,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -779,9 +779,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]]
name = "aws-config"
version = "1.5.4"
version = "1.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caf6cfe2881cb1fcbba9ae946fb9a6480d3b7a714ca84c74925014a89ef3387a"
checksum = "4e95816a168520d72c0e7680c405a5a8c1fb6a035b4bc4b9d7b0de8e1a941697"
dependencies = [
"aws-credential-types",
"aws-runtime",
@@ -799,7 +799,6 @@ dependencies = [
"fastrand 2.1.0",
"hex",
"http 0.2.12",
"hyper 0.14.30",
"ring 0.17.8",
"time",
"tokio",
@@ -822,9 +821,9 @@ dependencies = [
[[package]]
name = "aws-runtime"
version = "1.3.1"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87c5f920ffd1e0526ec9e70e50bf444db50b204395a0fa7016bbf9e31ea1698f"
checksum = "f42c2d4218de4dcd890a109461e2f799a1a2ba3bcd2cde9af88360f5df9266c6"
dependencies = [
"aws-credential-types",
"aws-sigv4",
@@ -837,6 +836,7 @@ dependencies = [
"fastrand 2.1.0",
"http 0.2.12",
"http-body 0.4.6",
"once_cell",
"percent-encoding",
"pin-project-lite",
"tracing",
@@ -845,9 +845,9 @@ dependencies = [
[[package]]
name = "aws-sdk-sso"
version = "1.36.0"
version = "1.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6acca681c53374bf1d9af0e317a41d12a44902ca0f2d1e10e5cb5bb98ed74f35"
checksum = "1074e818fbe4f9169242d78448b15be8916a79daa38ea1231f2e2e10d993fcd2"
dependencies = [
"aws-credential-types",
"aws-runtime",
@@ -867,9 +867,9 @@ dependencies = [
[[package]]
name = "aws-sdk-ssooidc"
version = "1.37.0"
version = "1.38.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b79c6bdfe612503a526059c05c9ccccbf6bd9530b003673cb863e547fd7c0c9a"
checksum = "29755c51e33fa3f678598f64324a169cf4b7d3c4865d2709d4308f53366a92a4"
dependencies = [
"aws-credential-types",
"aws-runtime",
@@ -889,9 +889,9 @@ dependencies = [
[[package]]
name = "aws-sdk-sts"
version = "1.36.0"
version = "1.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32e6ecdb2bd756f3b2383e6f0588dc10a4e65f5d551e70a56e0bfe0c884673ce"
checksum = "6e52dc3fd7dfa6c01a69cf3903e00aa467261639138a05b06cd92314d2c8fb07"
dependencies = [
"aws-credential-types",
"aws-runtime",
@@ -1264,7 +1264,7 @@ dependencies = [
"regex",
"rustc-hash 1.1.0",
"shlex",
"syn 2.0.72",
"syn 2.0.74",
"which 4.4.2",
]
@@ -1409,7 +1409,7 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.74",
"syn_derive",
]
@@ -1498,7 +1498,7 @@ checksum = "1ee891b04274a59bd38b412188e24b849617b2e45a0fd8d057deb63e7403761b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -1613,9 +1613,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.1.8"
version = "1.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "504bdec147f2cc13c8b57ed9401fd8a147cc66b67ad5cb241394244f2c947549"
checksum = "e9e8aabfac534be767c909e0690571677d49f41bd8465ae876fe043d52ba5292"
dependencies = [
"jobserver",
"libc",
@@ -1717,9 +1717,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.5.14"
version = "4.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c937d4061031a6d0c8da4b9a4f98a172fc2976dfb1c19213a9cf7d0d3c837e36"
checksum = "11d8838454fda655dafd3accb2b6e2bea645b9e4078abe84a22ceb947235c5cc"
dependencies = [
"clap_builder",
"clap_derive",
@@ -1727,9 +1727,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.5.14"
version = "4.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85379ba512b21a328adf887e85f7742d12e96eb31f3ef077df4ffc26b506ffed"
checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6"
dependencies = [
"anstream",
"anstyle",
@@ -1746,7 +1746,7 @@ dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -1946,9 +1946,9 @@ dependencies = [
[[package]]
name = "core-foundation-sys"
version = "0.8.6"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "cpufeatures"
@@ -2132,7 +2132,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -2180,7 +2180,7 @@ dependencies = [
"proc-macro2",
"quote",
"strsim 0.11.1",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -2202,7 +2202,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
dependencies = [
"darling_core 0.20.10",
"quote",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -2722,7 +2722,7 @@ dependencies = [
"quote",
"strum 0.25.0",
"strum_macros 0.25.3",
"syn 2.0.72",
"syn 2.0.74",
"thiserror",
]
@@ -2901,7 +2901,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustc_version 0.4.0",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -2998,7 +2998,7 @@ checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -3154,7 +3154,7 @@ dependencies = [
"heck 0.4.1",
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -3174,7 +3174,7 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -3285,14 +3285,14 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
[[package]]
name = "filetime"
version = "0.2.23"
version = "0.2.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd"
checksum = "bf401df4a4e3872c4fe8151134cf483738e74b67fc934d6532c882b3d24a4550"
dependencies = [
"cfg-if",
"libc",
"redox_syscall 0.4.1",
"windows-sys 0.52.0",
"libredox",
"windows-sys 0.59.0",
]
[[package]]
@@ -3377,7 +3377,7 @@ checksum = "32016f1242eb82af5474752d00fd8ebcd9004bd69b462b1c91de833972d08ed4"
dependencies = [
"proc-macro2",
"swc_macros_common",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -3405,7 +3405,7 @@ checksum = "b0fa992f1656e1707946bbba340ad244f0814009ef8c0118eb7b658395f19a2e"
dependencies = [
"frunk_proc_macro_helpers",
"quote",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -3417,7 +3417,7 @@ dependencies = [
"frunk_core",
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -3429,7 +3429,7 @@ dependencies = [
"frunk_core",
"frunk_proc_macro_helpers",
"quote",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -3553,7 +3553,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -3806,7 +3806,7 @@ checksum = "53010ccb100b96a67bc32c0175f0ed1426b31b655d562898e57325f81c023ac0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -4048,9 +4048,9 @@ dependencies = [
[[package]]
name = "hstr"
version = "0.2.11"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be89918d912f61134036c4fd49b8db2935b49cca4cbf45b3a6738abd2c6e9f77"
checksum = "dae404c0c5d4e95d4858876ab02eecd6a196bb8caa42050dfa809938833fc412"
dependencies = [
"hashbrown 0.14.5",
"new_debug_unreachable",
@@ -4450,7 +4450,7 @@ dependencies = [
"Inflector",
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -4699,6 +4699,7 @@ checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
dependencies = [
"bitflags 2.6.0",
"libc",
"redox_syscall 0.5.3",
]
[[package]]
@@ -5081,9 +5082,9 @@ dependencies = [
[[package]]
name = "mio"
version = "1.0.1"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4"
checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
dependencies = [
"hermit-abi 0.3.9",
"libc",
@@ -5109,7 +5110,7 @@ checksum = "a7ce64b975ed4f123575d11afd9491f2e37bbd5813fbfbc0f09ae1fbddea74e0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -5148,7 +5149,7 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.74",
"termcolor",
"thiserror",
]
@@ -5561,7 +5562,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -5879,7 +5880,7 @@ dependencies = [
"phf_shared",
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -5920,7 +5921,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -5937,9 +5938,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "piper"
version = "0.2.3"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae1d5c74c9876f070d3e8fd503d748c7d974c3e48da8f41350fa5222ef9b4391"
checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066"
dependencies = [
"atomic-waker",
"fastrand 2.1.0",
@@ -6013,9 +6014,9 @@ dependencies = [
[[package]]
name = "polling"
version = "3.7.2"
version = "3.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3ed00ed3fbf728b5816498ecd316d1716eecaced9c0c8d2c5a6740ca214985b"
checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511"
dependencies = [
"cfg-if",
"concurrent-queue",
@@ -6023,7 +6024,7 @@ dependencies = [
"pin-project-lite",
"rustix 0.38.34",
"tracing",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -6118,7 +6119,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e"
dependencies = [
"proc-macro2",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -6171,7 +6172,7 @@ checksum = "07c277e4e643ef00c1233393c673f655e3672cf7eb3ba08a00bdd0ea59139b5f"
dependencies = [
"proc-macro-rules-macros",
"proc-macro2",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -6183,7 +6184,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -6244,7 +6245,7 @@ dependencies = [
"schemars",
"serde",
"serde_json",
"syn 2.0.72",
"syn 2.0.74",
"thiserror",
"typify",
"unicode-ident",
@@ -6264,7 +6265,7 @@ dependencies = [
"serde_json",
"serde_tokenstream",
"serde_yaml",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -6307,7 +6308,7 @@ dependencies = [
"itertools 0.12.1",
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -6987,7 +6988,7 @@ dependencies = [
"proc-macro2",
"quote",
"rust-embed-utils",
"syn 2.0.72",
"syn 2.0.74",
"walkdir",
]
@@ -7369,7 +7370,7 @@ dependencies = [
"proc-macro2",
"quote",
"serde_derive_internals",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -7469,9 +7470,9 @@ checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4"
[[package]]
name = "serde"
version = "1.0.205"
version = "1.0.206"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e33aedb1a7135da52b7c21791455563facbbcc43d0f0f66165b42c21b3dfb150"
checksum = "5b3e4cd94123dd520a128bcd11e34d9e9e423e7e3e50425cb1b4b1e3549d0284"
dependencies = [
"serde_derive",
]
@@ -7510,13 +7511,13 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.205"
version = "1.0.206"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "692d6f5ac90220161d6774db30c662202721e64aed9058d2c394f451261420c1"
checksum = "fabfb6138d2383ea8208cf98ccf69cdfb1aff4088460681d84189aa259762f97"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -7527,14 +7528,14 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
name = "serde_json"
version = "1.0.122"
version = "1.0.124"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da"
checksum = "66ad62847a56b3dba58cc891acd13884b9c61138d330c0d7b6181713d4fce38d"
dependencies = [
"indexmap 2.3.0",
"itoa",
@@ -7602,7 +7603,7 @@ dependencies = [
"proc-macro2",
"quote",
"serde",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -7657,7 +7658,7 @@ dependencies = [
"darling 0.20.10",
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -8007,7 +8008,7 @@ checksum = "01b2e185515564f15375f593fb966b5718bc624ba77fe49fa4616ad619690554"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -8078,7 +8079,7 @@ dependencies = [
"quote",
"sqlx-core",
"sqlx-macros-core",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -8101,7 +8102,7 @@ dependencies = [
"sqlx-mysql",
"sqlx-postgres",
"sqlx-sqlite",
"syn 2.0.72",
"syn 2.0.74",
"tempfile",
"tokio",
"url",
@@ -8253,7 +8254,7 @@ dependencies = [
"proc-macro2",
"quote",
"swc_macros_common",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -8307,7 +8308,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustversion",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -8320,7 +8321,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustversion",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -8439,7 +8440,7 @@ dependencies = [
"proc-macro2",
"quote",
"swc_macros_common",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -8505,7 +8506,7 @@ dependencies = [
"proc-macro2",
"quote",
"swc_macros_common",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -8612,7 +8613,7 @@ dependencies = [
"proc-macro2",
"quote",
"swc_macros_common",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -8731,7 +8732,7 @@ checksum = "695a1d8b461033d32429b5befbf0ad4d7a2c4d6ba9cd5ba4e0645c615839e8e4"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -8742,7 +8743,7 @@ checksum = "91745f3561057493d2da768437c427c0e979dff7396507ae02f16c981c4a8466"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -8765,7 +8766,7 @@ dependencies = [
"proc-macro2",
"quote",
"swc_macros_common",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -8781,9 +8782,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.72"
version = "2.0.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af"
checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7"
dependencies = [
"proc-macro2",
"quote",
@@ -8799,7 +8800,7 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -8822,7 +8823,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -9066,7 +9067,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -9272,7 +9273,7 @@ dependencies = [
"backtrace",
"bytes",
"libc",
"mio 1.0.1",
"mio 1.0.2",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
@@ -9290,7 +9291,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -9571,7 +9572,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -9789,7 +9790,7 @@ dependencies = [
"regress",
"schemars",
"serde_json",
"syn 2.0.72",
"syn 2.0.74",
"thiserror",
"unicode-ident",
]
@@ -9806,7 +9807,7 @@ dependencies = [
"serde",
"serde_json",
"serde_tokenstream",
"syn 2.0.72",
"syn 2.0.74",
"typify-impl",
]
@@ -10009,9 +10010,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "ureq"
version = "2.10.0"
version = "2.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72139d247e5f97a3eff96229a7ae85ead5328a39efe76f8bf5a06313d505b6ea"
checksum = "b74fc6b57825be3373f7054754755f03ac3a8f5d70015ccad699ba2029956f4a"
dependencies = [
"base64 0.22.1",
"flate2",
@@ -10199,7 +10200,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.74",
"wasm-bindgen-shared",
]
@@ -10233,7 +10234,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.74",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@@ -10266,7 +10267,7 @@ checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -10400,7 +10401,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windmill"
version = "1.377.1"
version = "1.378.0"
dependencies = [
"anyhow",
"axum",
@@ -10440,7 +10441,7 @@ dependencies = [
[[package]]
name = "windmill-api"
version = "1.377.1"
version = "1.378.0"
dependencies = [
"anyhow",
"argon2",
@@ -10524,7 +10525,7 @@ dependencies = [
[[package]]
name = "windmill-api-client"
version = "1.377.1"
version = "1.378.0"
dependencies = [
"base64 0.21.7",
"chrono",
@@ -10542,7 +10543,7 @@ dependencies = [
[[package]]
name = "windmill-audit"
version = "1.377.1"
version = "1.378.0"
dependencies = [
"chrono",
"serde",
@@ -10555,7 +10556,7 @@ dependencies = [
[[package]]
name = "windmill-common"
version = "1.377.1"
version = "1.378.0"
dependencies = [
"anyhow",
"async-stream",
@@ -10596,7 +10597,7 @@ dependencies = [
[[package]]
name = "windmill-git-sync"
version = "1.377.1"
version = "1.378.0"
dependencies = [
"regex",
"rsmq_async",
@@ -10611,7 +10612,7 @@ dependencies = [
[[package]]
name = "windmill-indexer"
version = "1.377.1"
version = "1.378.0"
dependencies = [
"anyhow",
"bytes",
@@ -10632,7 +10633,7 @@ dependencies = [
[[package]]
name = "windmill-parser"
version = "1.377.1"
version = "1.378.0"
dependencies = [
"serde",
"serde_json",
@@ -10640,7 +10641,7 @@ dependencies = [
[[package]]
name = "windmill-parser-bash"
version = "1.377.1"
version = "1.378.0"
dependencies = [
"anyhow",
"lazy_static",
@@ -10651,7 +10652,7 @@ dependencies = [
[[package]]
name = "windmill-parser-go"
version = "1.377.1"
version = "1.378.0"
dependencies = [
"anyhow",
"gosyn",
@@ -10663,7 +10664,7 @@ dependencies = [
[[package]]
name = "windmill-parser-graphql"
version = "1.377.1"
version = "1.378.0"
dependencies = [
"anyhow",
"lazy_static",
@@ -10674,7 +10675,7 @@ dependencies = [
[[package]]
name = "windmill-parser-php"
version = "1.377.1"
version = "1.378.0"
dependencies = [
"anyhow",
"convert_case 0.6.0",
@@ -10688,7 +10689,7 @@ dependencies = [
[[package]]
name = "windmill-parser-py"
version = "1.377.1"
version = "1.378.0"
dependencies = [
"anyhow",
"itertools 0.13.0",
@@ -10699,7 +10700,7 @@ dependencies = [
[[package]]
name = "windmill-parser-py-imports"
version = "1.377.1"
version = "1.378.0"
dependencies = [
"anyhow",
"async-recursion",
@@ -10716,7 +10717,7 @@ dependencies = [
[[package]]
name = "windmill-parser-sql"
version = "1.377.1"
version = "1.378.0"
dependencies = [
"anyhow",
"lazy_static",
@@ -10727,7 +10728,7 @@ dependencies = [
[[package]]
name = "windmill-parser-ts"
version = "1.377.1"
version = "1.378.0"
dependencies = [
"anyhow",
"convert_case 0.6.0",
@@ -10746,7 +10747,7 @@ dependencies = [
[[package]]
name = "windmill-parser-wasm"
version = "1.377.1"
version = "1.378.0"
dependencies = [
"anyhow",
"getrandom 0.2.15",
@@ -10765,7 +10766,7 @@ dependencies = [
[[package]]
name = "windmill-queue"
version = "1.377.1"
version = "1.378.0"
dependencies = [
"anyhow",
"async-recursion",
@@ -10798,7 +10799,7 @@ dependencies = [
[[package]]
name = "windmill-sql-datatype-parser-wasm"
version = "1.377.1"
version = "1.378.0"
dependencies = [
"wasm-bindgen",
"wasm-bindgen-test",
@@ -10808,7 +10809,7 @@ dependencies = [
[[package]]
name = "windmill-worker"
version = "1.377.1"
version = "1.378.0"
dependencies = [
"anyhow",
"async-recursion",
@@ -11141,7 +11142,7 @@ checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.74",
"synstructure",
]
@@ -11163,7 +11164,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.74",
]
[[package]]
@@ -11183,7 +11184,7 @@ checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.74",
"synstructure",
]
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "windmill"
version = "1.377.1"
version = "1.378.0"
authors.workspace = true
edition.workspace = true
@@ -25,7 +25,7 @@ members = [
]
[workspace.package]
version = "1.377.1"
version = "1.378.0"
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
edition = "2021"
+1 -1
View File
@@ -1 +1 @@
9431876ca4dd2ce51a04b102557daa9f685dc5d5
80d9e507977b55d0b76272905c969c3d6e830083
+13 -1
View File
@@ -1,7 +1,7 @@
openapi: "3.0.3"
info:
version: 1.377.1
version: 1.378.0
title: Windmill API
contact:
@@ -845,6 +845,12 @@ paths:
operationId: renewLicenseKey
tags:
- setting
parameters:
- name: license_key
in: query
required: false
schema:
type: string
responses:
"200":
description: status
@@ -859,6 +865,12 @@ paths:
operationId: createCustomerPortalSession
tags:
- setting
parameters:
- name: license_key
in: query
required: false
schema:
type: string
responses:
"200":
description: url to portal
+2 -2
View File
@@ -350,7 +350,7 @@ pub async fn renew_license_key() -> Result<String> {
pub async fn renew_license_key(Extension(db): Extension<DB>, authed: ApiAuthed) -> Result<String> {
require_super_admin(&db, &authed.email).await?;
windmill_common::stats_ee::send_stats(&"manual".to_string(), &HTTP_CLIENT, &db).await?;
let result = windmill_common::ee::renew_license_key(&HTTP_CLIENT, &db).await;
let result = windmill_common::ee::renew_license_key(&HTTP_CLIENT, &db, None).await;
if result != "success" {
return Err(error::Error::BadRequest(format!(
@@ -371,7 +371,7 @@ pub async fn create_customer_portal_session() -> Result<String> {
#[cfg(feature = "enterprise")]
pub async fn create_customer_portal_session() -> Result<String> {
let url = windmill_common::ee::create_customer_portal_session(&HTTP_CLIENT).await?;
let url = windmill_common::ee::create_customer_portal_session(&HTTP_CLIENT, None).await?;
return Ok(url);
}
+6 -1
View File
@@ -34,7 +34,11 @@ pub async fn schedule_key_renewal(_http_client: &reqwest::Client, _db: &crate::d
}
#[cfg(feature = "enterprise")]
pub async fn renew_license_key(_http_client: &reqwest::Client, _db: &crate::db::DB) -> String {
pub async fn renew_license_key(
_http_client: &reqwest::Client,
_db: &crate::db::DB,
_key: Option<String>,
) -> String {
// Implementation is not open source
"".to_string()
}
@@ -42,6 +46,7 @@ pub async fn renew_license_key(_http_client: &reqwest::Client, _db: &crate::db::
#[cfg(feature = "enterprise")]
pub async fn create_customer_portal_session(
_http_client: &reqwest::Client,
_key: Option<String>,
) -> error::Result<String> {
// Implementation is not open source
Ok("".to_string())
+8
View File
@@ -14,6 +14,7 @@ use sqlx::{query_scalar, Postgres, Transaction};
use std::collections::HashMap;
use std::str::FromStr;
use windmill_common::db::Authed;
use windmill_common::ee::LICENSE_KEY_VALID;
use windmill_common::flows::Retry;
use windmill_common::jobs::JobPayload;
use windmill_common::schedule::schedule_to_user;
@@ -31,6 +32,13 @@ pub async fn push_scheduled_job<'c, R: rsmq_async::RsmqConnection + Send + 'c>(
schedule: &Schedule,
authed: Option<&Authed>,
) -> Result<QueueTransaction<'c, R>> {
if !*LICENSE_KEY_VALID.read().await {
return Err(error::Error::BadRequest(
"License key is not valid. Go to your superadmin settings to update your license key."
.to_string(),
));
}
let sched = cron::Schedule::from_str(schedule.schedule.as_ref())
.map_err(|e| error::Error::BadRequest(e.to_string()))?;
+1 -1
View File
@@ -2,7 +2,7 @@ import { sleep } from "https://deno.land/x/sleep@v1.2.1/mod.ts";
import * as windmill from "https://deno.land/x/windmill@v1.174.0/mod.ts";
import * as api from "https://deno.land/x/windmill@v1.174.0/windmill-api/index.ts";
export const VERSION = "v1.377.1";
export const VERSION = "v1.378.0";
export async function login(email: string, password: string): Promise<string> {
return await windmill.UserService.login({
+1 -1
View File
@@ -34,7 +34,7 @@ addEventListener("error", (event) => {
}
});
export const VERSION = "v1.377.1";
export const VERSION = "v1.378.0";
let command: any = new Command()
.name("wmill")
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "windmill-components",
"version": "1.377.1",
"version": "1.378.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "windmill-components",
"version": "1.377.1",
"version": "1.378.0",
"license": "AGPL-3.0",
"dependencies": {
"@aws-crypto/sha256-js": "^4.0.0",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "windmill-components",
"version": "1.377.1",
"version": "1.378.0",
"scripts": {
"dev": "vite dev",
"build": "vite build",
@@ -89,6 +89,10 @@
valueToken = undefined
await loadConnects()
manual = !connects?.includes(resourceType)
if (manual && expressOAuthSetup) {
dispatch('error', 'Express OAuth setup is not available for non OAuth resource types')
return
}
if (rt) {
if (!manual && expressOAuthSetup) {
await getScopesAndParams()
@@ -161,11 +165,14 @@
}
function popupListener(event) {
console.log('popupListener', event.data, event.origin, window.location.origin)
let data = event.data
if (event.origin !== window.location.origin) {
if (event.origin == null || event.origin !== window.location.origin) {
return
}
window.removeEventListener('message', popupListener)
if (data.type === 'error') {
sendUserToast(event.data.error, true)
step = 2
@@ -212,7 +219,7 @@
// if (!newPageOAuth) {
// window.location.href = url.toString()
// } else {
window.addEventListener('message', popupListener, { once: true })
window.addEventListener('message', popupListener)
window.open(url.toString(), '_blank', 'popup=true')
step += 1
+14 -2
View File
@@ -1,6 +1,6 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte'
import { format, isValid } from 'date-fns'
import { format, isValid, parse } from 'date-fns'
export let value: string | undefined = undefined
export let autofocus: boolean | null = false
@@ -8,10 +8,22 @@
export let maxDate: string | undefined = undefined
export let dateFormat: string | undefined = 'dd-MM-yyyy'
let date: string | undefined = undefined
let date: string | undefined = computeDate(value)
const dispatch = createEventDispatcher()
function computeDate(value: string | undefined) {
if (dateFormat === undefined) {
dateFormat = 'dd-MM-yyyy'
}
if (value) {
const res = format(parse(value, dateFormat, new Date()), 'yyyy-MM-dd')
return res
} else {
return undefined
}
}
function updateValue(newDate: string | undefined) {
if (newDate && isValid(new Date(newDate))) {
if (dateFormat === undefined) {
@@ -144,16 +144,27 @@
let resourceName = ''
function parseDate(license_key: string): string | undefined {
let splitted = license_key.split('.')
function parseLicenseKey(key: string): {
valid: boolean
expiration?: string
} {
let splitted = key.split('.')
if (splitted.length >= 3) {
try {
let i = parseInt(splitted[1])
let date = new Date(i * 1000)
return date.toLocaleDateString()
const stringDate = date.toLocaleDateString()
if (stringDate !== 'Invalid Date') {
return {
valid: date.getTime() > Date.now(),
expiration: date.toLocaleDateString()
}
}
} catch {}
}
return undefined
return {
valid: false
}
}
let to: string = ''
@@ -191,7 +202,9 @@
export async function renewLicenseKey() {
renewing = true
try {
await SettingService.renewLicenseKey()
await SettingService.renewLicenseKey({
licenseKey: values['license_key'] || undefined
})
sendUserToast('Key renewal successful')
loadSettings()
} catch (err) {
@@ -206,7 +219,9 @@
export async function openCustomerPortal() {
opening = true
try {
const url = await SettingService.createCustomerPortalSession()
const url = await SettingService.createCustomerPortalSession({
licenseKey: values['license_key'] || undefined
})
window.open(url, '_blank')
} finally {
opening = false
@@ -492,6 +507,7 @@
</div>
{/if}
{:else if setting.fieldType == 'license_key'}
{@const { valid, expiration } = parseLicenseKey(values[setting.key] ?? '')}
<div class="flex gap-2">
<Password
small
@@ -516,13 +532,25 @@
</div>
<div class="mt-1 flex flex-col gap-1 items-start">
{#if values[setting.key]?.length > 0}
{#if parseDate(values[setting.key])}
{#if valid}
<div class="flex flex-row gap-1 items-center">
<Info size={12} class="text-tertiary" />
<span class="text-tertiary text-xs"
>License key expires on {parseDate(values[setting.key])}</span
>License key expires on {expiration ?? ''}</span
>
</div>
{:else if expiration}
<div class="flex flex-row gap-1 items-center">
<AlertCircle size={12} class="text-red-600" />
<span class="text-red-600 text-xs"
>License key expired on {expiration}</span
>
</div>
{:else}
<div class="flex flex-row gap-1 items-center">
<AlertCircle size={12} class="text-red-600" />
<span class="text-red-600 text-xs">Invalid license key format</span>
</div>
{/if}
{/if}
{#if latestKeyRenewalAttempt}
@@ -581,7 +609,7 @@
</div>
{/if}
{#if $enterpriseLicense}
{#if valid || expiration}
<div class="flex flex-row gap-2 mt-1">
<Button
on:click={renewLicenseKey}
@@ -181,9 +181,17 @@
}
let prevDefaultValue: any = undefined
let defaultChange = 0
async function changeDefaultValue(inputCat, defaultValue) {
if (value == null || value == undefined || deepEqual(value, prevDefaultValue)) {
if (
value == null ||
value == undefined ||
deepEqual(value, prevDefaultValue) ||
(prevDefaultValue != undefined && !deepEqual(defaultValue, prevDefaultValue))
) {
value = defaultValue
defaultChange += 1
}
prevDefaultValue = structuredClone(defaultValue)
@@ -498,11 +506,13 @@
{/each}
</select>
{:else if inputCat == 'date'}
{#if format === 'date'}
<DateInput bind:value dateFormat={extra['dateFormat']} />
{:else}
<DateTimeInput useDropdown bind:value />
{/if}
{#key defaultChange}
{#if format === 'date'}
<DateInput bind:value dateFormat={extra['dateFormat']} />
{:else}
<DateTimeInput useDropdown bind:value />
{/if}
{/key}
{:else if inputCat == 'base64'}
<div class="flex flex-col my-6 w-full">
<input
@@ -10,6 +10,7 @@
import { Button, Drawer, DrawerContent } from './common'
import { Plus } from 'lucide-svelte'
import type { AppViewerContext } from './apps/types'
import { sendUserToast } from '$lib/toast'
const dispatch = createEventDispatcher()
@@ -17,7 +18,11 @@
export let value: string | undefined = initialValue
export let resourceType: string | undefined = undefined
export let disablePortal = false
export let expressOAuthSetup = false
export let disabled = false
let open = false
let refreshCount = 0
const appViewerContext = getContext<AppViewerContext>('AppViewerContext')
let valueSelect =
@@ -56,40 +61,64 @@
let drawer: Drawer | undefined = undefined
export function askNewResource() {
refreshCount += 1
open = true
}
function processEvent(event: MessageEvent) {
if (event.origin !== window.location.origin) {
return
}
if (event.data.type === 'error') {
sendUserToast(event.data.error, true)
}
if (event.data.type === 'refresh') {
window.removeEventListener('message', processEvent)
value = event.data.detail
valueSelect = { value, label: value }
drawer?.closeDrawer?.()
open = false
}
}
</script>
<DarkModeObserver bind:darkMode />
<Drawer bind:this={drawer} size="800px">
<DrawerContent
title="Add a Resource"
on:close={drawer.closeDrawer}
tooltip="Resources represent connections to third party systems. Learn more on how to integrate external APIs."
documentationLink="https://www.windmill.dev/docs/integrations/integrations_on_windmill"
>
<iframe
title="App connection"
class="w-full h-full"
src="{base}/embed_connect?resource_type={resourceType}&workspace={appViewerContext?.workspace ??
$workspaceStore}"
/>
</DrawerContent>
</Drawer>
{#if expressOAuthSetup}
{#if open}
{#key refreshCount}
<iframe
title="App connection"
class="w-full h-full hidden"
src="{base}/embed_connect?resource_type={resourceType}&workspace={appViewerContext?.workspace ??
$workspaceStore}&express=true"
/>
{/key}
{/if}
{:else}
<Drawer bind:this={drawer} size="800px">
<DrawerContent
title="Add a Resource"
on:close={drawer.closeDrawer}
tooltip="Resources represent connections to third party systems. Learn more on how to integrate external APIs."
documentationLink="https://www.windmill.dev/docs/integrations/integrations_on_windmill"
>
<iframe
title="App connection"
class="w-full h-full"
src="{base}/embed_connect?resource_type={resourceType}&workspace={appViewerContext?.workspace ??
$workspaceStore}&express=false"
/>
</DrawerContent>
</Drawer>
{/if}
<div class="flex flex-col w-full items-start">
<div class="flex flex-row gap-x-1 w-full">
<Select
{disabled}
portal={!disablePortal}
value={valueSelect}
on:change={(e) => {
@@ -111,14 +140,14 @@
{#if resourceType}
<Button
{disabled}
color="light"
variant="border"
size="xs"
on:click={() => {
open = true
window.removeEventListener('message', processEvent)
window.addEventListener('message', processEvent, {
once: true
})
window.addEventListener('message', processEvent)
drawer?.openDrawer?.()
}}
@@ -71,7 +71,10 @@
}
</script>
<div class={twMerge('w-full flex flex-col px-0.5 pb-2', largeGap ? 'gap-8' : 'gap-2')}>
<div
class={twMerge('w-full flex flex-col px-0.5 pb-2', largeGap ? 'gap-8' : 'gap-2')}
on:pointerdown
>
{#each keys as argName (argName)}
{#if typeof args == 'object' && schema?.properties[argName] && args}
<LightweightArgInput
+2 -1
View File
@@ -173,6 +173,7 @@
if (data.type === 'error') {
sendUserToast(event.data.error, true)
} else if (data.type === 'success') {
window.removeEventListener('message', popupListener)
dispatch('login')
}
}
@@ -187,7 +188,7 @@
let url = base + '/api/oauth/login/' + provider
if (popup) {
localStorage.setItem('closeUponLogin', 'true')
window.addEventListener('message', popupListener, { once: true })
window.addEventListener('message', popupListener)
window.open(url, '_blank', 'popup')
} else {
window.location.href = url
@@ -39,7 +39,7 @@
let collection = valueSelect ? [valueSelect] : []
export async function askNewResource() {
appConnect?.open?.(resourceType)
appConnect?.open?.(resourceType, expressOAuthSetup)
}
async function loadResources(resourceType: string | undefined) {
@@ -67,7 +67,7 @@
$: {
if (format == 'email') {
pattern = '^[\\w-.]+@([\\w-]+\\.)+[\\w-]{2,4}$'
pattern = '^[\\w-+.]+@([\\w-]+\\.)+[\\w-]{2,4}$'
}
}
@@ -126,20 +126,23 @@
<div
class={twMerge('p-2 overflow-auto h-full', css?.container?.class, 'wm-schema-form')}
style={css?.container?.style}
on:pointerdown|stopPropagation={(e) =>
!$connectingInput.opened && selectId(e, id, selectedComponent, $app)}
>
<LightweightSchemaForm
defaultValues={resolvedConfig.defaultValues}
dynamicEnums={resolvedConfig.dynamicEnums}
schema={result}
bind:isValid={valid}
bind:args
bind:this={schemaForm}
displayType={Boolean(resolvedConfig.displayType)}
largeGap={Boolean(resolvedConfig.largeGap)}
{css}
/>
<div
on:pointerdown|stopPropagation={(e) =>
!$connectingInput.opened && selectId(e, id, selectedComponent, $app)}
>
<LightweightSchemaForm
defaultValues={resolvedConfig.defaultValues}
dynamicEnums={resolvedConfig.dynamicEnums}
schema={result}
bind:isValid={valid}
bind:args
bind:this={schemaForm}
displayType={Boolean(resolvedConfig.displayType)}
largeGap={Boolean(resolvedConfig.largeGap)}
{css}
/>
</div>
</div>
{:else}
<p class="m-2 italic">Empty form (no property)</p>
@@ -14,7 +14,8 @@
import { components } from '../../editor/component'
import ResolveConfig from '../helpers/ResolveConfig.svelte'
import ResolveStyle from '../helpers/ResolveStyle.svelte'
import ResourcePicker from '$lib/components/ResourcePicker.svelte'
import { twMerge } from 'tailwind-merge'
import LightweightResourcePicker from '$lib/components/LightweightResourcePicker.svelte'
export let id: string
export let configuration: RichConfigurations
@@ -36,6 +37,15 @@
result: undefined as string | undefined
})
let css = initCss($app.css?.['userresourcecomponent'], customCss)
$: classInput = twMerge(
'windmillapp w-full px-2',
css?.input?.class ?? '',
'wm-input',
'wm-user-resource-select'
)
let value: string | undefined = outputs.result.peak()?.replace('$res:', '')
value && assignValue(outputs.result.peak())
@@ -66,9 +76,7 @@
}
}
let css = initCss($app.css?.['userresourcecomponent'], customCss)
let resourcePicker: ResourcePicker | undefined = undefined
let resourcePicker: LightweightResourcePicker | undefined = undefined
</script>
{#each Object.keys(components['userresourcecomponent'].initialData.configuration) as key (key)}
@@ -93,8 +101,11 @@
<InitializeComponent {id} />
{#if render}
<AlignWrapper {render} {verticalAlignment}>
<div class="relative w-full">
<ResourcePicker
<div
class="relative w-full {classInput}',
"
>
<LightweightResourcePicker
expressOAuthSetup={resolvedConfig.expressOauthSetup}
bind:this={resourcePicker}
{value}
@@ -935,7 +935,7 @@
<div class="mt-10" />
<h2>Secret public URL</h2>
<h2>Public URL</h2>
<div class="mt-4" />
<Toggle
@@ -951,7 +951,7 @@
/>
<div class="my-6 box">
Secret public url:
Public url:
{#if secretUrl}
{@const url = `${$page.url.hostname}/public/${$workspaceStore}/${secretUrl}`}
{@const href = $page.url.protocol + '//' + url}
@@ -971,7 +971,8 @@
{:else}<Loader2 class="animate-spin" />
{/if}
<div class="text-xs text-secondary"
>You may share this url directly or embed it using an iframe (if not requiring login)</div
>Share this url directly or embed it using an iframe (if requiring login, top-level domain
of embedding app must be the same as the one of Windmill)</div
>
</div>
@@ -1,7 +1,7 @@
<script lang="ts">
import { classNames } from '$lib/utils'
import type { AppViewerContext } from '../types'
import { Anchor, ArrowDownFromLine, Bug, Expand, Move, Network, Pen, Plug2 } from 'lucide-svelte'
import { Anchor, ArrowDownFromLine, Bug, Network, Pen, Plug2 } from 'lucide-svelte'
import { createEventDispatcher, getContext } from 'svelte'
import Popover from '$lib/components/Popover.svelte'
import { Button, Popup } from '$lib/components/common'
@@ -22,12 +22,40 @@
export let inlineEditorOpened: boolean = false
export let errorHandledByComponent: boolean = false
export let fullHeight: boolean = false
export let componentContainerWidth: number
//export let willNotDisplay: boolean = false
const DECISION_TREE_THRESHOLD = 300
const STEPPER_THRESHOLD = 180
const CONDITIONAL_WRAPPER_THRESHOLD = 200
const MINIMUM_WIDTH = 20
let maxWidth = 10
let isManuallySelected = false
let componentIsDebugging = false
$: maxWidth = Math.max(Math.round(0.2 * componentContainerWidth), MINIMUM_WIDTH)
const dispatch = createEventDispatcher()
const { errorByComponent, openDebugRun, connectingInput } =
getContext<AppViewerContext>('AppViewerContext')
// Function to check if any condition is met
function checkComponentOptions(): boolean {
const componentOptions = {
isEditable: hasInlineEditor,
isConditionalWrapper: component.type === 'conditionalwrapper',
hasTabs:
component.type === 'steppercomponent' ||
(component.type === 'tabscomponent' &&
component.configuration.tabsKind.type === 'static' &&
component.configuration.tabsKind.value === 'invisibleOnView'),
isDecisionTree: component.type === 'decisiontreecomponent'
}
return Object.values(componentOptions).some((value) => value)
}
</script>
{#if connecting}
@@ -54,142 +82,153 @@
{#if selected || hover}
<!-- svelte-ignore a11y-no-static-element-interactions -->
<!-- svelte-ignore a11y-mouse-events-have-key-events -->
<span
on:mouseover|stopPropagation={() => {
dispatch('mouseover')
}}
on:mousedown|stopPropagation|capture
draggable="false"
title={`Id: ${component.id}`}
class={twMerge(
'px-2 text-2xs font-semibold w-fit absolute shadow -top-[9px] -left-[8px] border rounded-sm z-50 cursor-move',
selected
? 'bg-indigo-500/90 border-indigo-600 text-white'
: $connectingInput.opened
? 'bg-red-500/90 border-red-600 text-white'
: 'bg-blue-500/90 border-blue-600 text-white'
)}
>
{component.id}
</span>
{/if}
{#if selected && !connecting}
<div class="top-[-9px] -right-[8px] flex flex-row absolute gap-1.5 z-50">
{#if hasInlineEditor}
<button
title="Edit"
class={classNames(
'px-1 text-2xs py-0.5 font-bold w-fit border cursor-pointer rounded-sm',
'bg-indigo-100 text-indigo-600 border-indigo-500 hover:bg-indigo-200 hover:text-indigo-800'
)}
on:click={() => dispatch('triggerInlineEditor')}
on:pointerdown|stopPropagation
>
{#if inlineEditorOpened}
<Pen aria-label="Unlock position" size={14} class="text-orange-500" />
{:else}
<Pen aria-label="Lock position" size={14} />
{/if}
</button>
{/if}
{#if component.type === 'conditionalwrapper'}
<TabsDebug id={component.id} tabs={component.conditions ?? []} isConditionalDebugMode />
{:else if component.type === 'steppercomponent' || (component.type === 'tabscomponent' && component.configuration.tabsKind.type === 'static' && component.configuration.tabsKind.value === 'invisibleOnView')}
<TabsDebug id={component.id} tabs={component.tabs ?? []} />
{:else if component.type === 'decisiontreecomponent'}
<button
title={'Open Decision Tree Editor'}
class={classNames(
'text-2xs py-0.5 px-1 font-bold w-fit border cursor-pointer rounded-sm',
'bg-indigo-100 text-indigo-600 border-indigo-500 hover:bg-indigo-200 hover:text-indigo-800'
)}
on:click={() => {
const element = document.getElementById(`decision-tree-graph-editor`)
if (element) {
element.click()
}
}}
on:pointerdown|stopPropagation
>
<Network size={14} />
</button>
<DecisionTreeDebug id={component.id} nodes={component.nodes ?? []} />
{/if}
<!-- {#if willNotDisplay}
<Popover>
<svelte:fragment slot="text">
This component won't render, because an other component above it is set to fill the
height.
</svelte:fragment>
<div
title="Fill height"
class={classNames(
'px-1 text-2xs py-0.5 font-bold w-fit border cursor-pointer rounded-sm',
'bg-red-100 text-red-600 border-red-500 hover:bg-red-200 hover:text-red-800'
)}
>
<EyeOff aria-label="Expand position" size={14} />
</div>
</Popover>
{/if} -->
<button
title="Fill height"
class={classNames(
'px-1 text-2xs py-0.5 font-bold w-fit border cursor-pointer rounded-sm',
'bg-indigo-100 text-indigo-600 border-indigo-500 hover:bg-indigo-200 hover:text-indigo-800'
)}
on:click={() => dispatch('fillHeight')}
on:pointerdown|stopPropagation
>
<ArrowDownFromLine
aria-label="Expand position"
size={14}
class={fullHeight ? 'text-orange-500' : ''}
/>
</button>
<button
title="Expand"
class={classNames(
'px-1 text-2xs py-0.5 font-bold w-fit border cursor-pointer rounded-sm',
'bg-indigo-100 text-indigo-600 border-indigo-500 hover:bg-indigo-200 hover:text-indigo-800'
)}
on:click={() => dispatch('expand')}
on:pointerdown|stopPropagation
>
<Expand aria-label="Expand position" size={14} />
</button>
<button
title="Lock Position"
class={classNames(
'px-1 text-2xs py-0.5 font-bold w-fit border rounded-sm cursor-pointer',
'bg-indigo-100 text-indigo-600 border-indigo-500 hover:bg-indigo-200 hover:text-indigo-800'
)}
on:click={() => dispatch('lock')}
on:pointerdown|stopPropagation
>
{#if locked}
<Anchor aria-label="Unlock position" size={14} class="text-orange-500" />
{:else}
<Anchor aria-label="Lock position" size={14} />
{/if}
</button>
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div class="-top-[18px] -left-[8px] flex flex-row flex-nowrap w-fit h-fit absolute gap-0.5">
<div
draggable="false"
title="Move"
on:mouseover|stopPropagation={() => {
dispatch('mouseover')
}}
on:mousedown|stopPropagation|capture
class={classNames(
'px-1 text-2xs py-0.5 font-bold w-fit border cursor-move rounded-sm',
'bg-indigo-100 text-indigo-600 border-indigo-500 hover:bg-indigo-200 hover:text-indigo-800',
'flex items-center justify-center'
draggable="false"
title={`Id: ${component.id}`}
class={twMerge(
'py-0.5 text-2xs w-fit h-full min-h-5 border rounded z-50 cursor-move flex flex-row flex-nowrap font-semibold items-center shadow',
selected
? 'bg-indigo-500/90 border-indigo-500 text-white'
: $connectingInput.opened
? 'bg-red-500/90 border-red-600 text-white'
: 'bg-blue-500/90 border-blue-600 text-white'
)}
>
<Move size={14} />
<div class={`px-1 text-2xs w-full min-w-4 h-full truncate`} style="max-width: {maxWidth}px;">
{component.id}
</div>
{#if !connecting}
<div class="flex flex-row px-1 py-0 gap-0.5">
<button
title="Fill height"
class={twMerge(
'px-1 py-0.5 text-2xs font-bold rounded cursor-pointer w-fit h-full',
fullHeight
? ' bg-indigo-800 text-indigo-200'
: 'text-white hover:bg-indigo-700 hover:text-indigo-200'
)}
on:click={() => dispatch('fillHeight')}
on:pointerdown|stopPropagation
>
<ArrowDownFromLine aria-label="Expand position" size={11} />
</button>
<button
title="Lock Position"
class={twMerge(
'px-1 py-0.5 text-2xs font-bold rounded cursor-pointer w-fit h-full',
locked
? ' bg-indigo-800 text-indigo-200'
: 'text-white hover:bg-indigo-700 hover:text-indigo-200'
)}
on:click={() => dispatch('lock')}
on:pointerdown|stopPropagation
>
{#if locked}
<Anchor aria-label="Unlock position" size={11} />
{:else}
<Anchor aria-label="Lock position" size={11} />
{/if}
</button>
</div>
{/if}
</div>
{#if selected && !connecting && checkComponentOptions()}
<div
class={twMerge(
'px-1 py-0.5 text-2xs font-semibold w-fit min-h-5 border shadow rounded z-50 flex flex-row items-center flex-nowrap',
isManuallySelected || componentIsDebugging
? 'bg-red-100 text-red-600 border-red-500'
: 'bg-indigo-100/90 border-indigo-200 text-indigo-600'
)}
>
{#if hasInlineEditor}
<button
title="Edit"
class={twMerge(
'px-1 py-0.5 text-2xs font-bold rounded cursor-pointer w-fit h-full',
inlineEditorOpened
? 'bg-indigo-300 text-indigo-800'
: 'text-indigo-600 hover:bg-indigo-300 hover:text-indigo-800'
)}
on:click={() => dispatch('triggerInlineEditor')}
on:pointerdown|stopPropagation
>
{#if inlineEditorOpened}
<Pen aria-label="Unlock position" size={11} />
{:else}
<Pen aria-label="Lock position" size={11} />
{/if}
</button>
{/if}
{#if component.type === 'conditionalwrapper'}
<TabsDebug
id={component.id}
tabs={component.conditions ?? []}
isConditionalDebugMode
isSmall={componentContainerWidth < CONDITIONAL_WRAPPER_THRESHOLD}
bind:isManuallySelected
/>
{:else if component.type === 'steppercomponent' || (component.type === 'tabscomponent' && component.configuration.tabsKind.type === 'static' && component.configuration.tabsKind.value === 'invisibleOnView')}
<TabsDebug
id={component.id}
tabs={component.tabs ?? []}
isSmall={componentContainerWidth < STEPPER_THRESHOLD}
bind:isManuallySelected
/>
{:else if component.type === 'decisiontreecomponent'}
<button
title={'Open Decision Tree Editor'}
class={twMerge(
'px-1 py-0.5 text-2xs font-bold rounded cursor-pointer w-fit h-full',
componentIsDebugging
? 'text-red-600 hover:bg-red-300 hover:text-red-800'
: 'text-indigo-600 hover:bg-indigo-300 hover:text-indigo-800'
)}
on:click={() => {
const element = document.getElementById(`decision-tree-graph-editor`)
if (element) {
element.click()
}
}}
on:pointerdown|stopPropagation
>
<Network size={11} />
</button>
<DecisionTreeDebug
id={component.id}
nodes={component.nodes ?? []}
isSmall={componentContainerWidth < DECISION_TREE_THRESHOLD}
bind:componentIsDebugging
/>
{/if}
<!-- {#if willNotDisplay}
<Popover>
<svelte:fragment slot="text">
This component won't render, because an other component above it is set to fill the
height.
</svelte:fragment>
<div
title="Fill height"
class={classNames(
'px-1 text-2xs py-0.5 font-bold w-fit border cursor-pointer rounded-sm',
'bg-red-100 text-red-600 border-red-500 hover:bg-red-200 hover:text-red-800'
)}
>
<EyeOff aria-label="Expand position" size={14} />
</div>
</Popover>
{/if} -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
</div>
{/if}
</div>
{/if}
@@ -6,10 +6,14 @@
import type { AppViewerContext } from '../types'
import type { DecisionTreeNode } from './component'
import { isDebugging } from './settingsPanel/decisionTree/utils'
import { X } from 'lucide-svelte'
import { X, Bug } from 'lucide-svelte'
export let nodes: DecisionTreeNode[] = []
export let id: string
export let isSmall = false
export let componentIsDebugging = false
$: componentIsDebugging = isDebugging($debuggingComponents, id)
const { componentControl, debuggingComponents, worldStore } =
getContext<AppViewerContext>('AppViewerContext')
@@ -71,20 +75,20 @@
<button
title={'Debug tabs'}
class={classNames(
'text-2xs py-0.5 font-bold w-fit border cursor-pointer rounded-sm',
isDebugging($debuggingComponents, id)
? 'bg-red-100 text-red-600 border-red-500 hover:bg-red-200 hover:text-red-800'
: 'bg-indigo-100 text-indigo-600 border-indigo-500 hover:bg-indigo-200 hover:text-indigo-800'
'px-1 text-2xs font-bold rounded cursor-pointer w-fit h-full',
componentIsDebugging
? ' hover:bg-red-300 hover:text-red-800'
: ' hover:bg-indigo-300 hover:text-indigo-800'
)}
on:click={() => dispatch('triggerInlineEditor')}
on:pointerdown|stopPropagation
>
<ButtonDropdown hasPadding={false}>
<svelte:fragment slot="buttonReplacement">
<div class="px-1">
{#if isDebugging($debuggingComponents, id)}
<div class="px-1 w-fit">
{#if componentIsDebugging}
<div class="flex flex-row items-center gap-2">
{`Debugging node ${nodes[$debuggingComponents[id] ?? 0]?.id}`}
{`${isSmall ? '' : 'Debugging node'} ${nodes[$debuggingComponents[id] ?? 0]?.id}`}
<button
on:click={() => {
$componentControl?.[id]?.setTab?.(0)
@@ -94,12 +98,14 @@
)
}}
>
<X size={14} />
<X size={11} />
</button>
</div>
{:else}
{`Debug nodes (current node: ${currentNodeId})`}
{/if}
{:else if isSmall}
<div class="flex h-full w-fit items-center"><Bug size={11} /></div>
{:else}<div class="whitespace-nowrap h-full"
>{`Debug nodes (current node: ${currentNodeId})`}</div
>{/if}
</div>
</svelte:fragment>
<svelte:fragment slot="items">
@@ -45,6 +45,23 @@
.flatMap((id) => dfs($app.grid, id, $app.subgrids ?? {}))
.filter((x) => x != undefined) as string[]
}
function handleLock(id: string) {
const gridItem = findGridItem($app, id)
if (gridItem) {
toggleFixed(gridItem)
}
$app = $app
}
function handleFillHeight(id: string) {
const gridItem = findGridItem($app, id)
const b = $breakpoint === 'sm' ? 3 : 12
if (gridItem?.[b]) {
gridItem[b].fullHeight = !gridItem[b].fullHeight
}
$app = $app
}
</script>
<div class="w-full z-[1000] overflow-visible h-full">
@@ -137,7 +154,23 @@
Boolean($selectedComponent?.includes(dataItem.id)) ? 'active-grid-item' : ''
)}
>
<GridEditorMenu id={dataItem.id}>
<GridEditorMenu
id={dataItem.id}
on:expand={() => {
push(history, $app)
$selectedComponent = [dataItem.id]
expandGriditem($app.grid, dataItem.id, $breakpoint)
$app = $app
}}
on:lock={() => {
handleLock(dataItem.id)
}}
on:fillHeight={() => {
handleFillHeight(dataItem.id)
}}
locked={isFixed(dataItem)}
fullHeight={dataItem?.[$breakpoint === 'sm' ? 3 : 12]?.fullHeight}
>
<Component
{hidden}
render={true}
@@ -146,25 +179,10 @@
locked={isFixed(dataItem)}
fullHeight={dataItem?.[$breakpoint === 'sm' ? 3 : 12]?.fullHeight}
on:lock={() => {
const gridItem = findGridItem($app, dataItem.id)
if (gridItem) {
toggleFixed(gridItem)
}
$app = $app
}}
on:expand={() => {
push(history, $app)
$selectedComponent = [dataItem.id]
expandGriditem($app.grid, dataItem.id, $breakpoint)
$app = $app
handleLock(dataItem.id)
}}
on:fillHeight={() => {
const gridItem = findGridItem($app, dataItem.id)
const b = $breakpoint === 'sm' ? 3 : 12
if (gridItem?.[b]) {
gridItem[b].fullHeight = !gridItem[b].fullHeight
}
$app = $app
handleFillHeight(dataItem.id)
}}
/>
</GridEditorMenu>
@@ -1,5 +1,6 @@
<script context="module" lang="ts">
import { writable } from 'svelte/store'
import Popover from '../../Popover.svelte'
interface ContextMenuRegistry {
id: string
@@ -13,9 +14,18 @@
<script lang="ts">
import { getModifierKey } from '$lib/utils'
import { Copy, Paintbrush2, Scissors, Trash } from 'lucide-svelte'
import {
Anchor,
ArrowDownFromLine,
Copy,
Expand,
ExternalLink,
Paintbrush2,
Scissors,
Trash
} from 'lucide-svelte'
import ComponentCallbacks from './component/ComponentCallbacks.svelte'
import { getContext } from 'svelte'
import { createEventDispatcher, getContext } from 'svelte'
import type { AppEditorContext, AppViewerContext } from '../types'
import DeleteComponent from './settingsPanel/DeleteComponent.svelte'
import { secondaryMenuLeft } from './settingsPanel/secondaryMenu'
@@ -27,6 +37,9 @@
let menuX = 0
let menuY = 0
export let locked: boolean = false
export let fullHeight: boolean = false
function handleRightClick(event: MouseEvent) {
event.preventDefault()
contextMenuVisible = true
@@ -61,11 +74,13 @@
const { selectedComponent } = getContext<AppViewerContext>('AppViewerContext')
const { movingcomponents, stylePanel } = getContext<AppEditorContext>('AppEditorContext')
const dispatch = createEventDispatcher()
let deleteComponent: DeleteComponent | undefined = undefined
const menuItems = [
{
label: 'Cut',
label: () => 'Cut',
onClick: () => {
componentCallbacks?.handleCut(new KeyboardEvent('keydown'))
},
@@ -74,7 +89,7 @@
disabled: $movingcomponents?.includes($selectedComponent?.[0] ?? '')
},
{
label: 'Copy',
label: () => 'Copy',
onClick: () => {
componentCallbacks?.handleCopy(new KeyboardEvent('keydown'))
},
@@ -82,16 +97,53 @@
shortcut: `${getModifierKey()}C`
},
{
label: 'Show style panel',
label: () => (fullHeight ? 'Undo fill height' : 'Fill height'),
onClick: () => {
dispatch('fillHeight')
},
icon: ArrowDownFromLine,
tooltip: {
text: 'When set to full height, a component will extend its height to fill the entire parent container (or canvas).',
link: 'https://www.windmill.dev/docs/apps/app_configuration_settings/app_styling#full-height'
}
},
{
label: () => 'Expand',
onClick: () => {
dispatch('expand')
},
icon: Expand,
tooltip: {
text: "Clicking the expand button maximizes the component's width and height, respecting other components' position.",
link: 'https://www.windmill.dev/docs/apps/canvas#expand-a-component'
}
},
{
label: () => (locked ? 'Unlock' : 'Lock'),
onClick: () => {
dispatch('lock')
},
icon: Anchor,
tooltip: {
text: 'Lock the component to prevent it from being repositioned by other components.',
link: 'https://www.windmill.dev/docs/apps/canvas#lock-the-position-of-a-component'
}
},
{
label: () => 'Show style panel',
onClick: () => {
secondaryMenuLeft?.toggle(stylePanel(), { type: 'style' })
},
icon: Paintbrush2,
disabled: $secondaryMenuLeft.isOpen
disabled: $secondaryMenuLeft.isOpen,
tooltip: {
text: 'Use style panel to define custom CSS and Tailwind classes for the components.',
link: 'https://www.windmill.dev/docs/apps/app_configuration_settings/app_styling'
}
},
{
label: 'Delete',
label: () => 'Delete',
onClick: () => {
deleteComponent?.removeGridElement()
},
@@ -122,30 +174,51 @@
{#each menuItems as item}
<!-- svelte-ignore a11y-no-static-element-interactions -->
<!-- svelte-ignore a11y-click-events-have-key-events -->
<button
class={twMerge(
'flex items-center p-2 hover:bg-surface-hover cursor-pointer transition-all rounded-md w-full',
item.color === 'red' && 'text-red-500',
item.color === 'green' && 'text-green-500',
item.color === 'blue' && 'text-blue-500',
item.disabled && 'opacity-50 cursor-not-allowed'
)}
on:click={() => {
item.onClick()
closeContextMenu()
}}
disabled={item.disabled}
>
<!-- svelte-ignore missing-declaration -->
<svelte:component this={item.icon} class="w-4 h-4" />
<span class="ml-2 text-xs">{item.label}</span>
{#if item.shortcut}
<span class="ml-auto text-xs text-gray-400">
{item.shortcut}
</span>
{/if}
</button>
<Popover
notClickable
placement="right"
popupClass="z-[7000]"
disablePopup={!item.tooltip}
appearTimeout={800}
>
<svelte:fragment slot="text"
>{item.tooltip?.text}
{#if item.tooltip?.link}
<a href={item.tooltip.link} target="_blank" class="text-blue-300 text-xs">
<div class="flex flex-row gap-2 mt-4">
See documentation
<ExternalLink size="16" />
</div>
</a>
{/if}</svelte:fragment
>
<button
class={twMerge(
'flex items-center p-2 hover:bg-surface-hover cursor-pointer transition-all rounded-md w-full',
item.color === 'red' && 'text-red-500',
item.color === 'green' && 'text-green-500',
item.color === 'blue' && 'text-blue-500',
item.disabled && 'opacity-50 cursor-not-allowed'
)}
on:click={() => {
item.onClick()
closeContextMenu()
}}
disabled={item.disabled}
>
<!-- svelte-ignore missing-declaration -->
<svelte:component this={item.icon} class="w-4 h-4" />
<span class="ml-2 text-xs">{item.label()}</span>
{#if item.shortcut}
<span class="ml-auto text-xs text-gray-400">
{item.shortcut}
</span>
{/if}
</button>
</Popover>
{/each}
</div>
</div>
@@ -4,26 +4,28 @@
import { MenuItem } from '@rgossiaux/svelte-headlessui'
import { createEventDispatcher, getContext } from 'svelte'
import type { AppViewerContext } from '../types'
import { Bug } from 'lucide-svelte'
export let tabs: any[] = []
export let id: string
export let isConditionalDebugMode: boolean = false
export let isSmall = false
const { componentControl } = getContext<AppViewerContext>('AppViewerContext')
const dispatch = createEventDispatcher()
let isManuallySelected: boolean = false
export let isManuallySelected: boolean = false
let selected: number | null = null
</script>
<button
title={isConditionalDebugMode ? 'Debug conditions' : 'Debug tabs'}
class={classNames(
'text-2xs py-0.5 font-bold w-fit border cursor-pointer rounded-sm',
'text-2xs font-bold w-fit h-full cursor-pointer rounded',
isManuallySelected
? 'bg-red-100 text-red-600 border-red-500 hover:bg-red-200 hover:text-red-800'
: 'bg-indigo-100 text-indigo-600 border-indigo-500 hover:bg-indigo-200 hover:text-indigo-800'
? 'hover:bg-red-200 hover:text-red-800'
: ' hover:text-indigo-800 hover:bg-indigo-300'
)}
on:click={() => dispatch('triggerInlineEditor')}
on:pointerdown|stopPropagation
@@ -32,14 +34,21 @@
<svelte:fragment slot="buttonReplacement">
<div class="px-1">
{#if isManuallySelected}
<div>
<div class="whitespace-nowrap">
{#if selected === tabs.length - 1}
{isConditionalDebugMode ? `Debug default condition` : `Debug tab ${selected + 1}`}
{#if isSmall}
{isConditionalDebugMode ? `df` : `t ${selected + 1}`}
{:else}
{isConditionalDebugMode ? `Debug default condition` : `Debug tab ${selected + 1}`}
{/if}
{:else if isSmall}
{`${isConditionalDebugMode ? 'c' : 't'} ${(selected ?? 0) + 1}`}
{:else}
{`Debugging ${isConditionalDebugMode ? 'condition' : 'tab'} ${(selected ?? 0) + 1}`}
{/if}
{`Debugging ${isConditionalDebugMode ? 'condition' : 'tab'} ${
(selected ?? 0) + 1
}`}{/if}
</div>
{:else}
{:else if isSmall}<Bug size={11} />{:else}
{isConditionalDebugMode ? `Debug conditions` : `Debug tabs`}
{/if}
</div>
@@ -244,10 +244,11 @@ export function createNewGridItem(
data: AppComponent,
columns?: Record<number, any>,
initialPosition: { x: number; y: number } = { x: 0, y: 0 },
recOverride?: Record<number, Size>
recOverride?: Record<number, Size>,
fixed?: boolean
): GridItem {
const newComponent = {
fixed: false,
fixed: fixed ?? false,
x: initialPosition.x,
y: initialPosition.y,
fullHeight: false
@@ -389,7 +390,9 @@ export function insertNewGridItem(
columns?: Record<string, any>,
keepId?: string,
initialPosition: { x: number; y: number } = { x: 0, y: 0 },
recOverride?: Record<number, Size>
recOverride?: Record<number, Size>,
keepSubgrids?: boolean,
fixed?: boolean
): string {
const id = keepId ?? getNextGridItemId(app)
@@ -404,7 +407,7 @@ export function insertNewGridItem(
}
// We only want to set subgrids when we are not moving
if (!keepId) {
if (!keepId || keepSubgrids) {
for (let i = 0; i < (data.numberOfSubgrids ?? 0); i++) {
app.subgrids[`${id}-${i}`] = []
}
@@ -436,7 +439,7 @@ export function insertNewGridItem(
let grid = focusedGrid ? app.subgrids[key!] : app.grid
const newItem = createNewGridItem(grid, id, data, columns, initialPosition, recOverride)
const newItem = createNewGridItem(grid, id, data, columns, initialPosition, recOverride, fixed)
grid.push(newItem)
return id
}
@@ -1094,7 +1097,7 @@ export function setUpTopBarComponentContent(id: string, app: App) {
subGridIndex: 0
},
undefined,
undefined,
'title',
undefined,
{
3: {
@@ -1118,7 +1121,7 @@ export function setUpTopBarComponentContent(id: string, app: App) {
subGridIndex: 0
},
undefined,
undefined,
'recomputeall',
undefined,
{
3: {
@@ -99,6 +99,7 @@
let initializing: boolean | undefined = undefined
let errorHandledByComponent: boolean = false
let componentContainerHeight: number = 0
let componentContainerWidth: number = 0
let inlineEditorOpened: boolean = false
@@ -158,6 +159,7 @@
inlineEditorOpened = !inlineEditorOpened
}}
{errorHandledByComponent}
{componentContainerWidth}
/>
{/if}
@@ -191,6 +193,7 @@
)}
style={$app.css?.['app']?.['component']?.style}
bind:clientHeight={componentContainerHeight}
bind:clientWidth={componentContainerWidth}
>
{#if component.type === 'displaycomponent'}
<AppDisplayComponent
@@ -2265,7 +2265,7 @@ This is a paragraph.
documentationLink: `${documentationBaseUrl}/resource_select`,
dims: '2:1-3:1' as AppComponentDimensions,
customCss: {
input: { style: '' }
input: { style: '', class: '' }
},
initialData: {
verticalAlignment: 'center',
@@ -27,6 +27,7 @@ export const authorizedClassnames = [
'wm-currency-input',
'wm-date-input',
'wm-text-input',
'wm-user-resource-select',
'wm-html',
'wm-table-container',
'wm-table-header',
@@ -335,6 +336,17 @@ export const customisationByComponent: Customisation[] = [
selectors: [{ selector: '.wm-text-input', comment: 'Text component', customCssKey: 'input' }],
variables: []
},
{
components: ['userresourcecomponent'],
selectors: [
{
selector: '.wm-user-resource-select',
comment: 'User resource select',
customCssKey: 'input'
}
],
variables: []
},
{
components: ['htmlcomponent'],
selectors: [{ selector: '.wm-html', comment: 'HTML component', customCssKey: 'container' }],
@@ -56,9 +56,9 @@ export function createAppFromScript(path: string, schema: Record<string, any> |
},
actions: [],
numberOfSubgrids: 1,
id: 'g'
id: 'topbar'
},
id: 'g'
id: 'topbar'
}
],
fullscreen: false,
@@ -410,7 +410,7 @@ export function createAppFromScript(path: string, schema: Record<string, any> |
id: 'f'
}
],
'g-0': [
'topbar-0': [
{
'3': {
fixed: false,
@@ -481,9 +481,9 @@ export function createAppFromScript(path: string, schema: Record<string, any> |
actions: [],
horizontalAlignment: 'left',
verticalAlignment: 'center',
id: 'h'
id: 'title'
},
id: 'h'
id: 'title'
},
{
'3': {
@@ -515,9 +515,9 @@ export function createAppFromScript(path: string, schema: Record<string, any> |
menuItems: [],
horizontalAlignment: 'right',
verticalAlignment: 'center',
id: 'i'
id: 'recomputeall'
},
id: 'i'
id: 'recomputeall'
}
]
}
@@ -592,7 +592,7 @@ export function createAppFromFlow(path: string, schema: Record<string, any> | un
},
{
'3': {
fixed: false,
fixed: true,
x: 0,
y: 8,
fullHeight: false,
@@ -600,7 +600,7 @@ export function createAppFromFlow(path: string, schema: Record<string, any> | un
h: 2
},
'12': {
fixed: false,
fixed: true,
x: 0,
y: 0,
fullHeight: false,
@@ -618,9 +618,9 @@ export function createAppFromFlow(path: string, schema: Record<string, any> | un
},
actions: [],
numberOfSubgrids: 1,
id: 'g'
id: 'topbar'
},
id: 'g'
id: 'topbar'
}
],
fullscreen: false,
@@ -973,7 +973,7 @@ export function createAppFromFlow(path: string, schema: Record<string, any> | un
id: 'f'
}
],
'g-0': [
'topbar-0': [
{
'3': {
fixed: false,
@@ -1044,9 +1044,9 @@ export function createAppFromFlow(path: string, schema: Record<string, any> | un
actions: [],
horizontalAlignment: 'left',
verticalAlignment: 'center',
id: 'h'
id: 'title'
},
id: 'h'
id: 'title'
},
{
'3': {
@@ -1078,9 +1078,9 @@ export function createAppFromFlow(path: string, schema: Record<string, any> | un
menuItems: [],
horizontalAlignment: 'right',
verticalAlignment: 'center',
id: 'i'
id: 'recomputeall'
},
id: 'i'
id: 'recomputeall'
}
]
}
@@ -82,7 +82,9 @@
$workspaceStore = queryWorkspace
}
menuHidden = $page.url.searchParams.get('nomenubar') === 'true'
menuHidden =
$page.url.searchParams.get('nomenubar') === 'true' ||
$page.url.pathname.startsWith('/oauth/callback/')
}
$: updateUserStore($workspaceStore)
@@ -130,12 +130,14 @@
}) as (id: string) => AppComponent,
undefined,
undefined,
undefined,
'topbar',
{ x: 0, y: 0 },
{
3: processDimension(preset.dims, 3),
12: processDimension(preset.dims, 12)
}
},
true,
true
)
setUpTopBarComponentContent(id, value)
@@ -5,6 +5,7 @@
import CenteredPage from '$lib/components/CenteredPage.svelte'
import PageHeader from '$lib/components/PageHeader.svelte'
import { Loader2 } from 'lucide-svelte'
import { sendUserToast } from '$lib/toast'
let client_name = $page.params.client_name
let error = $page.url.searchParams.get('error')
@@ -24,6 +25,7 @@
clientName: client_name,
requestBody: { code, state }
})
sendUserToast('successful', false)
window.opener.postMessage({ type: 'success', res, resource_type: client_name }, '*')
// goto(`/resources?resource_type=${client_name}`)
} catch (e) {
@@ -18,6 +18,7 @@
let darkMode: boolean = false
const workspace = $page.url.searchParams.get('workspace')
const express = $page.url.searchParams.get('express') == 'true'
if (workspace) {
$workspaceStore = workspace
@@ -25,7 +26,7 @@
onMount(async () => {
if (resourceType) {
appConnect?.open(resourceType)
appConnect?.open(resourceType, express)
}
})
</script>
@@ -67,6 +68,9 @@
bind:isGoogleSignin
bind:disabled
bind:manual
on:error={(e) => {
window?.parent?.postMessage({ type: 'error', error: e.detail }, '*')
}}
on:refresh={(e) => {
window?.parent?.postMessage({ type: 'refresh', detail: e.detail }, '*')
}}
+3 -3
View File
@@ -2,7 +2,7 @@ import { sveltekit } from '@sveltejs/kit/vite'
import { readFileSync } from 'fs'
import { fileURLToPath } from 'url'
import circleDependency from 'vite-plugin-circular-dependency'
import mkcert from 'vite-plugin-mkcert'
// import mkcert from 'vite-plugin-mkcert'
const file = fileURLToPath(new URL('package.json', import.meta.url))
const json = readFileSync(file, 'utf8')
@@ -11,7 +11,7 @@ const version = JSON.parse(json)
/** @type {import('vite').UserConfig} */
const config = {
server: {
https: true,
https: false,
port: 3000,
proxy: {
'^/api/.*': {
@@ -34,7 +34,7 @@ const config = {
preview: {
port: 3000
},
plugins: [mkcert(), sveltekit(), circleDependency({ circleImportThrowErr: false })],
plugins: [sveltekit(), circleDependency({ circleImportThrowErr: false })],
define: {
__pkg__: version
},
+2 -2
View File
@@ -4,8 +4,8 @@ verify_ssl = true
name = "pypi"
[packages]
wmill = ">=1.377.1"
wmill_pg = ">=1.377.1"
wmill = ">=1.378.0"
wmill_pg = ">=1.378.0"
sendgrid = "*"
mysql-connector-python = "*"
pymongo = "*"
+1 -1
View File
@@ -1,7 +1,7 @@
openapi: "3.0.3"
info:
version: 1.377.1
version: 1.378.0
title: OpenFlow Spec
contact:
name: Ruben Fiszel
@@ -12,7 +12,7 @@
RootModule = 'WindmillClient.psm1'
# Version number of this module.
ModuleVersion = '1.377.1'
ModuleVersion = '1.378.0'
# Supported PSEditions
# CompatiblePSEditions = @()
+1 -1
View File
@@ -1,6 +1,6 @@
[tool.poetry]
name = "wmill"
version = "1.377.1"
version = "1.378.0"
description = "A client library for accessing Windmill server wrapping the Windmill client API"
license = "Apache-2.0"
homepage = "https://windmill.dev"
+1 -1
View File
@@ -1,6 +1,6 @@
[tool.poetry]
name = "wmill-pg"
version = "1.377.1"
version = "1.378.0"
description = "An extension client for the wmill client library focused on pg"
license = "Apache-2.0"
homepage = "https://windmill.dev"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@windmill/windmill",
"version": "1.377.1",
"version": "1.378.0",
"exports": "./src/index.ts",
"publish": {
"exclude": ["!src", "./s3Types.ts", "./client.ts"]
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "windmill-client",
"description": "Windmill SDK client for browsers and Node.js",
"version": "1.377.1",
"version": "1.378.0",
"author": "Ruben Fiszel",
"license": "Apache 2.0",
"devDependencies": {
+1 -1
View File
@@ -1 +1 @@
1.377.1
1.378.0