Compare commits
54 Commits
app-previe
...
tutorials
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b76426ba83 | ||
|
|
1cb2a177c6 | ||
|
|
db87577a1b | ||
|
|
bc440f8d41 | ||
|
|
c219eb0ee9 | ||
|
|
1d5c194f09 | ||
|
|
7a9d230459 | ||
|
|
4d5e2499cf | ||
|
|
686275fd46 | ||
|
|
99399f4f77 | ||
|
|
6e09194313 | ||
|
|
7c825c212d | ||
|
|
bfe5b56c99 | ||
|
|
480fd781b6 | ||
|
|
4f2079f624 | ||
|
|
43c45d930c | ||
|
|
8d5c5b88a3 | ||
|
|
cc8bedd0c7 | ||
|
|
74c3d6443c | ||
|
|
c7be313210 | ||
|
|
ae53bafaf6 | ||
|
|
2ea15d5035 | ||
|
|
0f187d66dd | ||
|
|
4453690521 | ||
|
|
6691b19b24 | ||
|
|
2f9ccff65c | ||
|
|
09db6fd867 | ||
|
|
fd52740d5d | ||
|
|
6b0fb75d23 | ||
|
|
b1a45b1e70 | ||
|
|
b2de531a46 | ||
|
|
a4adcb5192 | ||
|
|
0c2cf92dd3 | ||
|
|
e6344dac6d | ||
|
|
8fb2454e83 | ||
|
|
3b6ae0cc49 | ||
|
|
96ff2eebc1 | ||
|
|
ed29d51c36 | ||
|
|
88e537ad1f | ||
|
|
b854ee3439 | ||
|
|
0a5e181a3a | ||
|
|
8cc59225d8 | ||
|
|
9c41346dde | ||
|
|
41a398f50e | ||
|
|
3436061ad4 | ||
|
|
569b5d2516 | ||
|
|
a08cdd7b86 | ||
|
|
719d475262 | ||
|
|
5b3e1183e5 | ||
|
|
7ed301b186 | ||
|
|
46b6e4371b | ||
|
|
e0d3465b07 | ||
|
|
7f8fe8dc17 | ||
|
|
24f58efd99 |
20
.github/workflows/deploy_to_windmill.yml
vendored
20
.github/workflows/deploy_to_windmill.yml
vendored
@@ -1,20 +0,0 @@
|
||||
name: Deploy to windmill.dev
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- "community/**"
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Deploy to windmill.dev
|
||||
uses: windmill-labs/windmill-gh-action-deploy@v2.0.0
|
||||
with:
|
||||
dry_run: false
|
||||
input_dir: community
|
||||
windmill_workspace: starter
|
||||
windmill_token: ${{ secrets.WINDMILL_API_TOKEN }}
|
||||
64
.github/workflows/docker-image.yml
vendored
64
.github/workflows/docker-image.yml
vendored
@@ -109,38 +109,38 @@ jobs:
|
||||
${{ steps.meta-ee-public.outputs.labels }}
|
||||
org.opencontainers.image.licenses=Windmill-Enterprise-License
|
||||
|
||||
|
||||
playwright:
|
||||
runs-on: [self-hosted, new]
|
||||
needs: [build]
|
||||
services:
|
||||
postgres:
|
||||
image: postgres
|
||||
env:
|
||||
POSTGRES_DB: windmill
|
||||
POSTGRES_USER: admin
|
||||
POSTGRES_PASSWORD: changeme
|
||||
ports:
|
||||
- 5432:5432
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: "Docker"
|
||||
run: echo "::set-output name=id::$(docker run --network=host --rm -d -p 8000:8000 --privileged -it -e DATABASE_URL=postgres://admin:changeme@localhost:5432/windmill -e BASE_INTERNAL_URL=http://localhost:8000 ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest)"
|
||||
id: docker-container
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
- name: "Playwright run"
|
||||
timeout-minutes: 2
|
||||
run: cd frontend && npm ci @playwright/test && npx playwright install && export BASE_URL=http://localhost:8000 && npm run test
|
||||
- name: "Clean up"
|
||||
run: docker kill ${{ steps.docker-container.outputs.id }}
|
||||
if: always()
|
||||
# disabled until we make it 100% reliable and add more meaningful tests
|
||||
# playwright:
|
||||
# runs-on: [self-hosted, new]
|
||||
# needs: [build]
|
||||
# services:
|
||||
# postgres:
|
||||
# image: postgres
|
||||
# env:
|
||||
# POSTGRES_DB: windmill
|
||||
# POSTGRES_USER: admin
|
||||
# POSTGRES_PASSWORD: changeme
|
||||
# ports:
|
||||
# - 5432:5432
|
||||
# options: >-
|
||||
# --health-cmd pg_isready
|
||||
# --health-interval 10s
|
||||
# --health-timeout 5s
|
||||
# --health-retries 5
|
||||
# steps:
|
||||
# - uses: actions/checkout@v3
|
||||
# - name: "Docker"
|
||||
# run: echo "::set-output name=id::$(docker run --network=host --rm -d -p 8000:8000 --privileged -it -e DATABASE_URL=postgres://admin:changeme@localhost:5432/windmill -e BASE_INTERNAL_URL=http://localhost:8000 ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest)"
|
||||
# id: docker-container
|
||||
# - uses: actions/setup-node@v3
|
||||
# with:
|
||||
# node-version: 16
|
||||
# - name: "Playwright run"
|
||||
# timeout-minutes: 2
|
||||
# run: cd frontend && npm ci @playwright/test && npx playwright install && export BASE_URL=http://localhost:8000 && npm run test
|
||||
# - name: "Clean up"
|
||||
# run: docker kill ${{ steps.docker-container.outputs.id }}
|
||||
# if: always()
|
||||
|
||||
|
||||
publish_privately_heavy:
|
||||
|
||||
63
CHANGELOG.md
63
CHANGELOG.md
@@ -1,6 +1,69 @@
|
||||
# Changelog
|
||||
|
||||
|
||||
## [1.74.2](https://github.com/windmill-labs/windmill/compare/v1.74.1...v1.74.2) (2023-03-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **frontend:** fix splitpanes navigation ([#1276](https://github.com/windmill-labs/windmill/issues/1276)) ([8d5c5b8](https://github.com/windmill-labs/windmill/commit/8d5c5b88a35d7a3bad1d8ddf2d940026825241eb))
|
||||
|
||||
## [1.74.1](https://github.com/windmill-labs/windmill/compare/v1.74.0...v1.74.1) (2023-03-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **apps:** proper reactivity for non rendered static components ([ae53baf](https://github.com/windmill-labs/windmill/commit/ae53bafaf6777f928113f84b2c6ed6a2ed341844))
|
||||
* **ci:** make windmill compile again by pinning swc deps ([2ea15d5](https://github.com/windmill-labs/windmill/commit/2ea15d5035e5e15473968db3c0501a4dddff5cd0))
|
||||
|
||||
## [1.74.0](https://github.com/windmill-labs/windmill/compare/v1.73.1...v1.74.0) (2023-03-09)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add delete by path for scripts ([0c2cf92](https://github.com/windmill-labs/windmill/commit/0c2cf92dd3df9610e649f15e23921a4ca0d94e6a))
|
||||
* **frontend:** Add color picker input to app ([#1270](https://github.com/windmill-labs/windmill/issues/1270)) ([88e537a](https://github.com/windmill-labs/windmill/commit/88e537ad1fb4c207f38fbe951c82106bef6491a3))
|
||||
* **frontend:** add expand ([#1268](https://github.com/windmill-labs/windmill/issues/1268)) ([b854ee3](https://github.com/windmill-labs/windmill/commit/b854ee34393534bde104e2e6f606108fd66d38dc))
|
||||
* **frontend:** add hash to ctx in apps ([b1a45b1](https://github.com/windmill-labs/windmill/commit/b1a45b1e708aa6f19f8be9c949507083e044f2d8))
|
||||
* **frontend:** Add key navigation in app editor ([#1273](https://github.com/windmill-labs/windmill/issues/1273)) ([6b0fb75](https://github.com/windmill-labs/windmill/commit/6b0fb75d23e2151c88b07814139d203c1bd0578d))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **cli:** improve visibility of the active workspace ([e6344da](https://github.com/windmill-labs/windmill/commit/e6344dac6d1be04b46231fa8ef8579fd12ca8f37))
|
||||
* **frontend:** add confirmation modal to delete script/flow/app ([a4adcb5](https://github.com/windmill-labs/windmill/commit/a4adcb5192c11f7bf47a0d259825e474779378d7))
|
||||
* **frontend:** Clean up app editor ([#1267](https://github.com/windmill-labs/windmill/issues/1267)) ([0a5e181](https://github.com/windmill-labs/windmill/commit/0a5e181a3aa966fb8211bee0d9174fc16353b31f))
|
||||
* **frontend:** Minor changes ([#1272](https://github.com/windmill-labs/windmill/issues/1272)) ([3b6ae0c](https://github.com/windmill-labs/windmill/commit/3b6ae0cc49461b858d9cfff79eae9a7569465235))
|
||||
* **frontend:** simplify input bindings ([b2de531](https://github.com/windmill-labs/windmill/commit/b2de531a46e4b120d7106d361b727746bec516dd))
|
||||
|
||||
## [1.73.1](https://github.com/windmill-labs/windmill/compare/v1.73.0...v1.73.1) (2023-03-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **frontend:** load flow is not initialized ([719d475](https://github.com/windmill-labs/windmill/commit/719d4752621d462b1cfaa0d27930fba7586be779))
|
||||
|
||||
## [1.73.0](https://github.com/windmill-labs/windmill/compare/v1.72.0...v1.73.0) (2023-03-07)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **frontend:** add a way to automatically resize ([#1259](https://github.com/windmill-labs/windmill/issues/1259)) ([24f58ef](https://github.com/windmill-labs/windmill/commit/24f58efd9994a2201c1b1d9bbfb11734c57068e3))
|
||||
* **frontend:** add ability to move nodes ([614fb50](https://github.com/windmill-labs/windmill/commit/614fb5022aa7d5428fb96b7ee3a20794edd1e9d3))
|
||||
* **frontend:** Add app PDF viewer ([#1254](https://github.com/windmill-labs/windmill/issues/1254)) ([3e5d09e](https://github.com/windmill-labs/windmill/commit/3e5d09ef0b5619186bee5ec6d442cbfd12a6e8d5))
|
||||
* **frontend:** add fork/save buttons + consistent styling for slider/range ([9e9f8ef](https://github.com/windmill-labs/windmill/commit/9e9f8efb8ee389ea75e99b67ef720756959ca737))
|
||||
* **frontend:** add history to flows and apps ([9e4d90a](https://github.com/windmill-labs/windmill/commit/9e4d90ad37a57ff1f515eea0c82cf603649e915d))
|
||||
* **frontend:** Fix object viewer style ([#1255](https://github.com/windmill-labs/windmill/issues/1255)) ([94f1aad](https://github.com/windmill-labs/windmill/commit/94f1aadef2b09ac1962478f11b27cc708b8328f1))
|
||||
* **frontend:** refactor entire flow builder UX ([2ac51b0](https://github.com/windmill-labs/windmill/commit/2ac51b0af08bdef7ce3c7e874e9983b9fc00478a))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **frontend:** arginput + apppreview fixes ([e2c4545](https://github.com/windmill-labs/windmill/commit/e2c45452401022b00285b21551ffaf35a114be33))
|
||||
* **frontend:** fix app map reactivity ([#1260](https://github.com/windmill-labs/windmill/issues/1260)) ([2557e13](https://github.com/windmill-labs/windmill/commit/2557e136bd0df1a023819b7d9b2235e30d7140b6))
|
||||
* **frontend:** fix branch deletion ([#1261](https://github.com/windmill-labs/windmill/issues/1261)) ([a999eb2](https://github.com/windmill-labs/windmill/commit/a999eb21121a7c0010621448324e0c77caf2b3f6))
|
||||
* **frontend:** Side menu z-index issue ([#1265](https://github.com/windmill-labs/windmill/issues/1265)) ([c638897](https://github.com/windmill-labs/windmill/commit/c638897fdcd58f55b0929f91641b21a6f9d25ead))
|
||||
|
||||
## [1.72.0](https://github.com/windmill-labs/windmill/compare/v1.71.0...v1.72.0) (2023-03-02)
|
||||
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ ARG features=""
|
||||
|
||||
COPY --from=planner /windmill/recipe.json recipe.json
|
||||
|
||||
RUN CARGO_NET_GIT_FETCH_WITH_CLI=true cargo chef cook --release --features "$features" --recipe-path recipe.json
|
||||
RUN CARGO_NET_GIT_FETCH_WITH_CLI=true RUST_BACKTRACE=1 cargo chef cook --release --features "$features" --recipe-path recipe.json
|
||||
|
||||
COPY ./openflow.openapi.yaml /openflow.openapi.yaml
|
||||
COPY ./backend ./
|
||||
@@ -86,6 +86,7 @@ RUN CARGO_NET_GIT_FETCH_WITH_CLI=true cargo build --release --features "$feature
|
||||
|
||||
|
||||
FROM python:3.11.2-slim-buster
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
ARG APP=/usr/src/app
|
||||
|
||||
@@ -129,6 +130,10 @@ COPY --from=nsjail /nsjail/nsjail /bin/nsjail
|
||||
|
||||
COPY --from=denoland/deno:latest /usr/bin/deno /usr/bin/deno
|
||||
|
||||
# docker does not support conditional COPY and we want to use the same Dockerfile for both amd64 and arm64 and privilege the official image
|
||||
COPY --from=lukechannings/deno:latest /usr/bin/deno /usr/bin/deno-arm
|
||||
RUN if [ "$TARGETPLATFORM" = "linux/amd64" ]; then rm /usr/bin/deno-arm; elif [ "$TARGETPLATFORM" = "linux/arm64" ]; then mv /usr/bin/deno-arm /usr/bin/deno; fi
|
||||
|
||||
RUN mkdir -p ${APP}
|
||||
|
||||
WORKDIR ${APP}
|
||||
|
||||
245
backend/Cargo.lock
generated
245
backend/Cargo.lock
generated
@@ -67,9 +67,9 @@ checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800"
|
||||
|
||||
[[package]]
|
||||
name = "argon2"
|
||||
version = "0.4.1"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db4ce4441f99dbd377ca8a8f57b698c44d0d6e712d8329b5040da5a64aa1ce73"
|
||||
checksum = "95c2fcf79ad1932ac6269a738109997a83c227c09b75842ae564dc8ede6a861c"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"blake2",
|
||||
@@ -202,9 +202,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.64"
|
||||
version = "0.1.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2"
|
||||
checksum = "b84f9ebcc6c1f5b8cb160f6990096a5c127f423fcb6e1ccc46c370cbdfb75dfc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -254,9 +254,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "axum"
|
||||
version = "0.6.9"
|
||||
version = "0.6.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6137c6234afb339e75e764c866e3594900f0211e1315d33779f269bbe2ec6967"
|
||||
checksum = "2fb79c228270dcf2426e74864cabc94babb5dbab01a4314e702d2f16540e1591"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum-core",
|
||||
@@ -281,16 +281,16 @@ dependencies = [
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tower",
|
||||
"tower-http",
|
||||
"tower-http 0.3.5",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum-core"
|
||||
version = "0.3.2"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cae3e661676ffbacb30f1a824089a8c9150e71017f7e1e38f2aa32009188d34"
|
||||
checksum = "b2f958c80c248b34b9a877a643811be8dbca03ca5ba827f2b63baf3a81e5fc4e"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bytes",
|
||||
@@ -378,9 +378,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.3"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e"
|
||||
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
@@ -719,9 +719,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxx"
|
||||
version = "1.0.91"
|
||||
version = "1.0.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86d3488e7665a7a483b57e25bdd90d0aeb2bc7608c8d0346acf2ad3f1caf1d62"
|
||||
checksum = "9a140f260e6f3f79013b8bfc65e7ce630c9ab4388c6a89c71e07226f49487b72"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cxxbridge-flags",
|
||||
@@ -731,9 +731,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxx-build"
|
||||
version = "1.0.91"
|
||||
version = "1.0.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48fcaf066a053a41a81dfb14d57d99738b767febb8b735c3016e469fac5da690"
|
||||
checksum = "da6383f459341ea689374bf0a42979739dc421874f112ff26f829b8040b8e613"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"codespan-reporting",
|
||||
@@ -746,15 +746,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-flags"
|
||||
version = "1.0.91"
|
||||
version = "1.0.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2ef98b8b717a829ca5603af80e1f9e2e48013ab227b68ef37872ef84ee479bf"
|
||||
checksum = "90201c1a650e95ccff1c8c0bb5a343213bdd317c6e600a93075bca2eff54ec97"
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-macro"
|
||||
version = "1.0.91"
|
||||
version = "1.0.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892"
|
||||
checksum = "0b75aed41bb2e6367cae39e6326ef817a851db13c13e4f3263714ca3cfb8de56"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -895,7 +895,7 @@ version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
|
||||
dependencies = [
|
||||
"block-buffer 0.10.3",
|
||||
"block-buffer 0.10.4",
|
||||
"const-oid",
|
||||
"crypto-common",
|
||||
"subtle",
|
||||
@@ -1612,9 +1612,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "io-lifetimes"
|
||||
version = "1.0.5"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3"
|
||||
checksum = "cfa919a82ea574332e2de6e74b4c36e74d41982b335080fa59d4ef31be20fdf3"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.45.0",
|
||||
@@ -1662,9 +1662,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.5"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
|
||||
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
@@ -1815,9 +1815,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.139"
|
||||
version = "0.2.140"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
|
||||
checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c"
|
||||
|
||||
[[package]]
|
||||
name = "libgit2-sys"
|
||||
@@ -2263,9 +2263,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "password-hash"
|
||||
version = "0.4.2"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700"
|
||||
checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"rand_core 0.6.4",
|
||||
@@ -2274,9 +2274,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.11"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba"
|
||||
checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79"
|
||||
|
||||
[[package]]
|
||||
name = "pem-rfc7468"
|
||||
@@ -2295,9 +2295,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
|
||||
|
||||
[[package]]
|
||||
name = "pest"
|
||||
version = "2.5.5"
|
||||
version = "2.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "028accff104c4e513bad663bbcd2ad7cfd5304144404c31ed0a77ac103d00660"
|
||||
checksum = "8cbd939b234e95d72bc393d51788aec68aeeb5d51e748ca08ff3aad58cb722f7"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
"ucd-trie",
|
||||
@@ -2525,7 +2525,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "progenitor"
|
||||
version = "0.2.1-dev"
|
||||
source = "git+https://github.com/oxidecomputer/progenitor#cbe875ad1a4c650cf3af595c90df6fd7421b47c2"
|
||||
source = "git+https://github.com/oxidecomputer/progenitor#e9b6970d4485ad5813f93e95570480195528b0a9"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"built",
|
||||
@@ -2543,7 +2543,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "progenitor-client"
|
||||
version = "0.2.1-dev"
|
||||
source = "git+https://github.com/oxidecomputer/progenitor#cbe875ad1a4c650cf3af595c90df6fd7421b47c2"
|
||||
source = "git+https://github.com/oxidecomputer/progenitor#e9b6970d4485ad5813f93e95570480195528b0a9"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
@@ -2557,7 +2557,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "progenitor-impl"
|
||||
version = "0.2.1-dev"
|
||||
source = "git+https://github.com/oxidecomputer/progenitor#cbe875ad1a4c650cf3af595c90df6fd7421b47c2"
|
||||
source = "git+https://github.com/oxidecomputer/progenitor#e9b6970d4485ad5813f93e95570480195528b0a9"
|
||||
dependencies = [
|
||||
"getopts",
|
||||
"heck",
|
||||
@@ -2579,7 +2579,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "progenitor-macro"
|
||||
version = "0.2.1-dev"
|
||||
source = "git+https://github.com/oxidecomputer/progenitor#cbe875ad1a4c650cf3af595c90df6fd7421b47c2"
|
||||
source = "git+https://github.com/oxidecomputer/progenitor#e9b6970d4485ad5813f93e95570480195528b0a9"
|
||||
dependencies = [
|
||||
"openapiv3",
|
||||
"proc-macro2",
|
||||
@@ -2846,9 +2846,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed"
|
||||
version = "6.4.2"
|
||||
version = "6.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "283ffe2f866869428c92e0d61c2f35dfb4355293cdfdc48f49e895c15f1333d1"
|
||||
checksum = "cb133b9a38b5543fad3807fb2028ea47c5f2b566f4f5e28a11902f1a358348b6"
|
||||
dependencies = [
|
||||
"rust-embed-impl",
|
||||
"rust-embed-utils",
|
||||
@@ -2857,9 +2857,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed-impl"
|
||||
version = "6.3.1"
|
||||
version = "6.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31ab23d42d71fb9be1b643fe6765d292c5e14d46912d13f3ae2815ca048ea04d"
|
||||
checksum = "4d4e0f0ced47ded9a68374ac145edd65a6c1fa13a96447b873660b2a568a0fd7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2870,9 +2870,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed-utils"
|
||||
version = "7.3.0"
|
||||
version = "7.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1669d81dfabd1b5f8e2856b8bbe146c6192b0ba22162edc738ac0a5de18f054"
|
||||
checksum = "512b0ab6853f7e14e3c8754acb43d6f748bb9ced66aa5915a6553ac8213f7731"
|
||||
dependencies = [
|
||||
"sha2 0.10.6",
|
||||
"walkdir",
|
||||
@@ -2917,9 +2917,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.36.8"
|
||||
version = "0.36.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644"
|
||||
checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
@@ -2953,7 +2953,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-ast"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython#6e4c2fe7866f705bb427200a36d6c9fb46508de3"
|
||||
source = "git+https://github.com/RustPython/RustPython#87728c44527272ece739cddbb4942ad7e176dd79"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"rustpython-compiler-core",
|
||||
@@ -2962,7 +2962,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-compiler-core"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython#6e4c2fe7866f705bb427200a36d6c9fb46508de3"
|
||||
source = "git+https://github.com/RustPython/RustPython#87728c44527272ece739cddbb4942ad7e176dd79"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bstr",
|
||||
@@ -2975,7 +2975,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-parser"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython#6e4c2fe7866f705bb427200a36d6c9fb46508de3"
|
||||
source = "git+https://github.com/RustPython/RustPython#87728c44527272ece739cddbb4942ad7e176dd79"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"anyhow",
|
||||
@@ -2998,15 +2998,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.11"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70"
|
||||
checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.12"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
|
||||
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
@@ -3066,9 +3066,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "scratch"
|
||||
version = "1.0.3"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2"
|
||||
checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1"
|
||||
|
||||
[[package]]
|
||||
name = "sct"
|
||||
@@ -3147,9 +3147,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.152"
|
||||
version = "1.0.154"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
|
||||
checksum = "8cdd151213925e7f1ab45a9bbfb129316bd00799784b174b7cc7bcd16961c49e"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
@@ -3176,9 +3176,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.152"
|
||||
version = "1.0.154"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
|
||||
checksum = "4fc80d722935453bcafdc2c9a73cd6fac4dc1938f0346035d84bf99fa9e33217"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3198,9 +3198,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.93"
|
||||
version = "1.0.94"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76"
|
||||
checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"itoa",
|
||||
@@ -3210,9 +3210,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_path_to_error"
|
||||
version = "0.1.9"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26b04f22b563c91331a10074bda3dd5492e3cc39d56bd557e91c0af42b6c7341"
|
||||
checksum = "db0969fff533976baadd92e08b1d102c5a3d8a8049eadfd69d4d1e3c5b2ed189"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@@ -3241,9 +3241,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_tokenstream"
|
||||
version = "0.1.6"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "274f512d6748a01e67cbcde5b4307ab2c9d52a98a2b870a980ef0793a351deff"
|
||||
checksum = "797ba1d80299b264f3aac68ab5d12e5825a561749db4df7cd7c8083900c5d4e9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"serde",
|
||||
@@ -3278,9 +3278,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_yaml"
|
||||
version = "0.9.17"
|
||||
version = "0.9.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fb06d4b6cdaef0e0c51fa881acb721bed3c924cfaa71d9c94a3b771dfdf6567"
|
||||
checksum = "f82e6c8c047aa50a7328632d067bcae6ef38772a79e28daf32f735e0e4f3dd10"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"itoa",
|
||||
@@ -3384,6 +3384,17 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smartstring"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"static_assertions",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smol_str"
|
||||
version = "0.1.24"
|
||||
@@ -3395,9 +3406,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.4.7"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd"
|
||||
checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
@@ -3581,9 +3592,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "string_cache"
|
||||
version = "0.8.4"
|
||||
version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "213494b7a2b503146286049378ce02b482200519accc31872ee8be91fa820a08"
|
||||
checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b"
|
||||
dependencies = [
|
||||
"new_debug_unreachable",
|
||||
"once_cell",
|
||||
@@ -3642,9 +3653,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
||||
|
||||
[[package]]
|
||||
name = "swc_atoms"
|
||||
version = "0.4.38"
|
||||
version = "0.4.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a172f2e444ae1378286cd27ff2a5cb26eadfd7a77c98ccb0edde8992857be1e"
|
||||
checksum = "2ebef84c2948cd0d1ba25acbf1b4bd9d80ab6f057efdbe35d8449b8d54699401"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"rustc-hash",
|
||||
@@ -3656,9 +3667,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "swc_common"
|
||||
version = "0.29.33"
|
||||
version = "0.29.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8a75c46065858a37cdda2c1c6fd056986e3b7752d5ec332e91ce312c6a22749"
|
||||
checksum = "2d515be281f603cb97afaa89896aca5e5c748fde11c7f926e35cdaa8ff8da705"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"ast_node",
|
||||
@@ -3683,9 +3694,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "swc_ecma_ast"
|
||||
version = "0.98.0"
|
||||
version = "0.98.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b1f6bc913d0f1daf0fa713a64660aed27848e0047ee671ab2206ad602730d1f"
|
||||
checksum = "305d2aa5e36a775e0d376552b35b325fccf0d7d8c633da1e4aec8e16460251cf"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"is-macro",
|
||||
@@ -3700,9 +3711,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "swc_ecma_parser"
|
||||
version = "0.127.0"
|
||||
version = "0.128.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3875db58a1515382c670679062133294e5ec88aff08776f5fb6e4f3c09c0f5f9"
|
||||
checksum = "35e349e3f4c5561645b9042caae162dbaf55502be7b583ac99f3ccf3e65bccb7"
|
||||
dependencies = [
|
||||
"either",
|
||||
"enum_kind",
|
||||
@@ -3710,6 +3721,7 @@ dependencies = [
|
||||
"num-bigint",
|
||||
"serde",
|
||||
"smallvec",
|
||||
"smartstring",
|
||||
"stacker",
|
||||
"swc_atoms",
|
||||
"swc_common",
|
||||
@@ -3818,18 +3830,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.38"
|
||||
version = "1.0.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
|
||||
checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.38"
|
||||
version = "1.0.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
|
||||
checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -4095,6 +4107,25 @@ dependencies = [
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-http"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-range-header",
|
||||
"pin-project-lite",
|
||||
"tower",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-http"
|
||||
version = "0.4.0"
|
||||
@@ -4109,7 +4140,6 @@ dependencies = [
|
||||
"http-body",
|
||||
"http-range-header",
|
||||
"pin-project-lite",
|
||||
"tower",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
@@ -4244,7 +4274,7 @@ checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
|
||||
[[package]]
|
||||
name = "typify"
|
||||
version = "0.0.11-dev"
|
||||
source = "git+https://github.com/oxidecomputer/typify#05d65ea62be9061c1abd0c31d955d0248120d301"
|
||||
source = "git+https://github.com/oxidecomputer/typify#18e7faf1957812626e3bf459e20fbe16785de2d4"
|
||||
dependencies = [
|
||||
"typify-impl",
|
||||
"typify-macro",
|
||||
@@ -4253,7 +4283,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "typify-impl"
|
||||
version = "0.0.11-dev"
|
||||
source = "git+https://github.com/oxidecomputer/typify#05d65ea62be9061c1abd0c31d955d0248120d301"
|
||||
source = "git+https://github.com/oxidecomputer/typify#18e7faf1957812626e3bf459e20fbe16785de2d4"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"log",
|
||||
@@ -4271,7 +4301,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "typify-macro"
|
||||
version = "0.0.11-dev"
|
||||
source = "git+https://github.com/oxidecomputer/typify#05d65ea62be9061c1abd0c31d955d0248120d301"
|
||||
source = "git+https://github.com/oxidecomputer/typify#18e7faf1957812626e3bf459e20fbe16785de2d4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -4362,9 +4392,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.10"
|
||||
version = "0.3.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58"
|
||||
checksum = "524b68aca1d05e03fdf03fcdce2c6c94b6daf6d16861ddaa7e4f2b6638a9052c"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-general-category"
|
||||
@@ -4380,9 +4410,9 @@ checksum = "d70b6494226b36008c8366c288d77190b3fad2eb4c10533139c1c1f461127f1a"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.6"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
|
||||
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
@@ -4420,16 +4450,16 @@ checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
|
||||
[[package]]
|
||||
name = "unicode_names2"
|
||||
version = "0.6.0"
|
||||
source = "git+https://github.com/youknowone/unicode_names2.git?tag=v0.6.0+character-alias#4ce16aa85cbcdd9cc830410f1a72ef9a235f2fde"
|
||||
source = "git+https://github.com/youknowone/unicode_names2.git?rev=4ce16aa85cbcdd9cc830410f1a72ef9a235f2fde#4ce16aa85cbcdd9cc830410f1a72ef9a235f2fde"
|
||||
dependencies = [
|
||||
"phf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unsafe-libyaml"
|
||||
version = "0.2.5"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc7ed8ba44ca06be78ea1ad2c3682a43349126c8818054231ee6f4748012aed2"
|
||||
checksum = "ad2024452afd3874bf539695e04af6732ba06517424dbf958fdb16a01f3bef6c"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
@@ -4712,7 +4742,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windmill"
|
||||
version = "1.72.0"
|
||||
version = "1.74.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
@@ -4739,7 +4769,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api"
|
||||
version = "1.72.0"
|
||||
version = "1.74.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argon2",
|
||||
@@ -4778,7 +4808,7 @@ dependencies = [
|
||||
"tokio-util",
|
||||
"tower",
|
||||
"tower-cookies",
|
||||
"tower-http",
|
||||
"tower-http 0.4.0",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"urlencoding",
|
||||
@@ -4794,7 +4824,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-client"
|
||||
version = "1.72.0"
|
||||
version = "1.74.2"
|
||||
dependencies = [
|
||||
"base64 0.21.0",
|
||||
"chrono",
|
||||
@@ -4809,7 +4839,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-audit"
|
||||
version = "1.72.0"
|
||||
version = "1.74.2"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"serde",
|
||||
@@ -4822,7 +4852,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-common"
|
||||
version = "1.72.0"
|
||||
version = "1.74.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
@@ -4847,7 +4877,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser"
|
||||
version = "1.72.0"
|
||||
version = "1.74.2"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -4855,13 +4885,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-bash"
|
||||
version = "1.72.0"
|
||||
version = "1.74.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools",
|
||||
"lazy_static",
|
||||
"phf",
|
||||
"regex",
|
||||
"serde_json",
|
||||
"unicode-general-category",
|
||||
"windmill-common",
|
||||
"windmill-parser",
|
||||
@@ -4869,7 +4900,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-go"
|
||||
version = "1.72.0"
|
||||
version = "1.74.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools",
|
||||
@@ -4881,7 +4912,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-py"
|
||||
version = "1.72.0"
|
||||
version = "1.74.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools",
|
||||
@@ -4896,7 +4927,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-ts"
|
||||
version = "1.72.0"
|
||||
version = "1.74.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"deno_core",
|
||||
@@ -4910,7 +4941,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-queue"
|
||||
version = "1.72.0"
|
||||
version = "1.74.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@@ -4933,7 +4964,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-worker"
|
||||
version = "1.72.0"
|
||||
version = "1.74.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -5049,9 +5080,9 @@ checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.3.3"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "faf09497b8f8b5ac5d3bb4d05c0a99be20f26fd3d5f2db7b0716e946d5103658"
|
||||
checksum = "ee7b2c67f962bf5042bfd8b6a916178df33a26eec343ae064cb8e069f638fa6f"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "windmill"
|
||||
version = "1.72.0"
|
||||
version = "1.74.2"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
@@ -19,7 +19,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.72.0"
|
||||
version = "1.74.2"
|
||||
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
|
||||
edition = "2021"
|
||||
|
||||
@@ -123,8 +123,8 @@ regex = "^1"
|
||||
deno_core = "^0"
|
||||
async-recursion = "^1"
|
||||
swc_common = "^0"
|
||||
swc_ecma_parser = "^0"
|
||||
swc_ecma_ast = "^0"
|
||||
swc_ecma_parser = "0.128.2"
|
||||
swc_ecma_ast = "0.98.1"
|
||||
base64 = "0.21.0"
|
||||
unicode-general-category = "^0"
|
||||
hmac = "0.12.1"
|
||||
|
||||
@@ -16,4 +16,5 @@ unicode-general-category.workspace = true
|
||||
itertools.workspace = true
|
||||
anyhow.workspace = true
|
||||
regex.workspace = true
|
||||
lazy_static.workspace = true
|
||||
lazy_static.workspace = true
|
||||
serde_json.workspace = true
|
||||
@@ -1,6 +1,8 @@
|
||||
#![allow(non_snake_case)] // TODO: switch to parse_* function naming
|
||||
|
||||
use anyhow::anyhow;
|
||||
use regex::Regex;
|
||||
use serde_json::json;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use windmill_parser::{Arg, MainArgSignature, Typ};
|
||||
@@ -17,19 +19,32 @@ pub fn parse_bash_sig(code: &str) -> windmill_common::error::Result<MainArgSigna
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref RE: Regex = Regex::new(r#"(?m)^(\w+)="\$(?:(\d+)|\{(\d+):-(.*)\})"$"#).unwrap();
|
||||
}
|
||||
|
||||
fn parse_file(code: &str) -> anyhow::Result<Option<Vec<Arg>>> {
|
||||
let mut hm = HashMap::new();
|
||||
let re = Regex::new(r#"(?m)^(\w+)="\$(\d+)"$"#).unwrap();
|
||||
for cap in re.captures_iter(code) {
|
||||
hm.insert(cap[2].parse::<i32>()?, cap[1].to_string());
|
||||
let mut hm: HashMap<i32, (String, Option<String>)> = HashMap::new();
|
||||
for cap in RE.captures_iter(code) {
|
||||
hm.insert(
|
||||
cap.get(2)
|
||||
.or(cap.get(3))
|
||||
.and_then(|x| x.as_str().parse::<i32>().ok())
|
||||
.ok_or_else(|| anyhow!("Impossible to parse arg digit"))?,
|
||||
(
|
||||
cap[1].to_string(),
|
||||
cap.get(4).map(|x| x.as_str().to_string()),
|
||||
),
|
||||
);
|
||||
}
|
||||
let mut args = vec![];
|
||||
for i in 1..20 {
|
||||
if hm.contains_key(&i) {
|
||||
let (name, default) = hm.get(&i).unwrap();
|
||||
args.push(Arg {
|
||||
name: hm[&i].clone(),
|
||||
name: name.clone(),
|
||||
typ: Typ::Str(None),
|
||||
default: None,
|
||||
default: default.clone().map(|x| json!(x)),
|
||||
otyp: None,
|
||||
has_default: false,
|
||||
});
|
||||
@@ -43,6 +58,8 @@ fn parse_file(code: &str) -> anyhow::Result<Option<Vec<Arg>>> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use serde_json::json;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
@@ -50,8 +67,7 @@ mod tests {
|
||||
let code = r#"
|
||||
token="$1"
|
||||
image="$2"
|
||||
digest="${3:-latest}"
|
||||
foo="$4"
|
||||
digest="${3:-latest with spaces}"
|
||||
|
||||
"#;
|
||||
//println!("{}", serde_json::to_string()?);
|
||||
@@ -74,6 +90,13 @@ foo="$4"
|
||||
typ: Typ::Str(None),
|
||||
default: None,
|
||||
has_default: false
|
||||
},
|
||||
Arg {
|
||||
otyp: None,
|
||||
name: "digest".to_string(),
|
||||
typ: Typ::Str(None),
|
||||
default: Some(json!("latest with spaces")),
|
||||
has_default: false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -468,6 +468,27 @@
|
||||
},
|
||||
"query": "DELETE FROM workspace_settings WHERE workspace_id = $1"
|
||||
},
|
||||
"0e7d95f4913e5775651971d741a3b5c1ef5dfe079be5325abe2866d39a7fe5fb": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "path",
|
||||
"ordinal": 0,
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
false
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "DELETE FROM script WHERE path = $1 AND workspace_id = $2 RETURNING path"
|
||||
},
|
||||
"11b1586acdfc180c5a077861ee1f7201fcbcec9d0ebada464f9d952c9c3e400d": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
@@ -5019,6 +5040,18 @@
|
||||
},
|
||||
"query": "\n SELECT id, flow_status, suspend, script_path\n FROM queue\n WHERE id = ( SELECT parent_job FROM queue WHERE id = $1 UNION ALL SELECT parent_job FROM completed_job WHERE id = $1)\n FOR UPDATE\n "
|
||||
},
|
||||
"c9d97800eb0ec87df8e8959b283dacb2c6cce422365ed394375641488ceb6b65": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "UPDATE worker_ping SET ping_at = now() WHERE worker = $1"
|
||||
},
|
||||
"cac594031a21b4806de9c4616317d3541522ef9712a83ecff7bd8b5f6e870748": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: "3.0.3"
|
||||
|
||||
info:
|
||||
version: 1.72.0
|
||||
version: 1.74.2
|
||||
title: Windmill API
|
||||
|
||||
contact:
|
||||
@@ -2278,7 +2278,7 @@ paths:
|
||||
|
||||
/w/{workspace}/scripts/delete/h/{hash}:
|
||||
post:
|
||||
summary: delete script by hash (erase content but keep hash)
|
||||
summary: delete script by hash (erase content but keep hash, require admin)
|
||||
operationId: deleteScriptByHash
|
||||
tags:
|
||||
- script
|
||||
@@ -2293,6 +2293,23 @@ paths:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Script"
|
||||
|
||||
/w/{workspace}/scripts/delete/p/{path}:
|
||||
post:
|
||||
summary: delete all scripts at a given path (require admin)
|
||||
operationId: deleteScriptByPath
|
||||
tags:
|
||||
- script
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/ScriptPath"
|
||||
responses:
|
||||
"200":
|
||||
description: script path
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/w/{workspace}/scripts/get/p/{path}:
|
||||
get:
|
||||
summary: get script by path
|
||||
|
||||
@@ -313,10 +313,13 @@ async fn create_app(
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Extension(webhook): Extension<WebhookShared>,
|
||||
Path(w_id): Path<String>,
|
||||
Json(app): Json<CreateApp>,
|
||||
Json(mut app): Json<CreateApp>,
|
||||
) -> Result<(StatusCode, String)> {
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
|
||||
app.policy.on_behalf_of = Some(username_to_permissioned_as(&authed.username));
|
||||
app.policy.on_behalf_of_email = Some(authed.email);
|
||||
|
||||
let id = sqlx::query_scalar!(
|
||||
"INSERT INTO app
|
||||
(workspace_id, path, summary, policy, versions)
|
||||
@@ -463,7 +466,9 @@ async fn update_app(
|
||||
sqlb.set_str("summary", nsummary);
|
||||
}
|
||||
|
||||
if let Some(npolicy) = ns.policy {
|
||||
if let Some(mut npolicy) = ns.policy {
|
||||
npolicy.on_behalf_of = Some(username_to_permissioned_as(&authed.username));
|
||||
npolicy.on_behalf_of_email = Some(authed.email);
|
||||
sqlb.set(
|
||||
"policy",
|
||||
&format!(
|
||||
@@ -728,6 +733,9 @@ fn build_args(
|
||||
path: String,
|
||||
args: &Map<String, Value>,
|
||||
) -> Result<Map<String, Value>> {
|
||||
// disallow var and res access in args coming from the user for security reasons
|
||||
args.into_iter()
|
||||
.try_for_each(|x| disallow_var_res_access(x.1))?;
|
||||
let static_args = policy
|
||||
.triggerables
|
||||
.get(&path)
|
||||
@@ -748,3 +756,20 @@ fn build_args(
|
||||
}
|
||||
Ok(args)
|
||||
}
|
||||
|
||||
fn disallow_var_res_access(args: &serde_json::Value) -> Result<()> {
|
||||
match args {
|
||||
Value::Object(v) => v.into_iter().try_for_each(|x| disallow_var_res_access(x.1)),
|
||||
Value::Array(arr) => arr.into_iter().try_for_each(|v| disallow_var_res_access(v)),
|
||||
Value::String(s) => {
|
||||
if s.starts_with("$var:") || s.starts_with("$res:") {
|
||||
Err(Error::BadRequest(format!(
|
||||
"For security reasons, variable or resource access is not allowed as dynamic argument"
|
||||
)))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -640,8 +640,6 @@ mod tests {
|
||||
"type": "script",
|
||||
"path": "test"
|
||||
},
|
||||
"stop_after_if": null,
|
||||
"summary": null
|
||||
},
|
||||
{
|
||||
"id": "b",
|
||||
@@ -650,15 +648,12 @@ mod tests {
|
||||
"input_transforms": {},
|
||||
"type": "rawscript",
|
||||
"content": "test",
|
||||
"lock": null,
|
||||
"path": null,
|
||||
"language": "deno"
|
||||
},
|
||||
"stop_after_if": {
|
||||
"expr": "foo = 'bar'",
|
||||
"skip_if_stopped": false
|
||||
},
|
||||
"summary": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "c",
|
||||
@@ -680,8 +675,7 @@ mod tests {
|
||||
"stop_after_if": {
|
||||
"expr": "previous.isEmpty()",
|
||||
"skip_if_stopped": false,
|
||||
},
|
||||
"summary": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"failure_module": {
|
||||
@@ -695,8 +689,7 @@ mod tests {
|
||||
"stop_after_if": {
|
||||
"expr": "previous.isEmpty()",
|
||||
"skip_if_stopped": false
|
||||
},
|
||||
"summary": null
|
||||
}
|
||||
}
|
||||
});
|
||||
assert_eq!(dbg!(serde_json::json!(fv)), dbg!(expect));
|
||||
|
||||
@@ -70,6 +70,7 @@ pub fn workspaced_service() -> Router {
|
||||
.route("/exists/p/*path", get(exists_script_by_path))
|
||||
.route("/archive/h/:hash", post(archive_script_by_hash))
|
||||
.route("/delete/h/:hash", post(delete_script_by_hash))
|
||||
.route("/delete/p/*path", post(delete_script_by_path))
|
||||
.route("/get/h/:hash", get(get_script_by_hash))
|
||||
.route("/raw/h/:hash", get(raw_script_by_hash))
|
||||
.route("/deployment_status/h/:hash", get(get_deployment_status))
|
||||
@@ -720,6 +721,46 @@ async fn delete_script_by_hash(
|
||||
Ok(Json(script))
|
||||
}
|
||||
|
||||
async fn delete_script_by_path(
|
||||
authed: Authed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Extension(webhook): Extension<WebhookShared>,
|
||||
Extension(db): Extension<DB>,
|
||||
Path((w_id, path)): Path<(String, StripPath)>,
|
||||
) -> JsonResult<String> {
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
let path = path.to_path();
|
||||
|
||||
require_admin(authed.is_admin, &authed.username)?;
|
||||
let script = sqlx::query_scalar!(
|
||||
"DELETE FROM script WHERE path = $1 AND workspace_id = $2 RETURNING path",
|
||||
path,
|
||||
w_id
|
||||
)
|
||||
.fetch_one(&db)
|
||||
.await
|
||||
.map_err(|e| Error::InternalErr(format!("deleting script by path {w_id}: {e}")))?;
|
||||
|
||||
audit_log(
|
||||
&mut tx,
|
||||
&authed.username,
|
||||
"scripts.delete",
|
||||
ActionKind::Delete,
|
||||
&w_id,
|
||||
Some(&path),
|
||||
Some([("workspace", w_id.as_str())].into()),
|
||||
)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
|
||||
webhook.send_message(
|
||||
w_id.clone(),
|
||||
WebhookMessage::DeleteScriptPath { workspace: w_id, path: path.to_string() },
|
||||
);
|
||||
|
||||
Ok(Json(script))
|
||||
}
|
||||
|
||||
async fn parse_python_code_to_jsonschema(
|
||||
Json(code): Json<String>,
|
||||
) -> JsonResult<windmill_parser::MainArgSignature> {
|
||||
|
||||
@@ -37,6 +37,7 @@ pub enum WebhookMessage {
|
||||
CreateScript { workspace: String, path: String, hash: String },
|
||||
UpdateScript { workspace: String, path: String, hash: String },
|
||||
DeleteScript { workspace: String, hash: String },
|
||||
DeleteScriptPath { workspace: String, path: String },
|
||||
CreateVariable { workspace: String, path: String },
|
||||
UpdateVariable { workspace: String, old_path: String, new_path: String },
|
||||
DeleteVariable { workspace: String, path: String },
|
||||
|
||||
@@ -34,7 +34,7 @@ use tokio::{
|
||||
io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader},
|
||||
process::{Child, Command},
|
||||
sync::{
|
||||
mpsc::{self, Sender}, watch,
|
||||
mpsc::{self, Sender}, watch, broadcast,
|
||||
},
|
||||
time::{interval, sleep, Instant, MissedTickBehavior},
|
||||
};
|
||||
@@ -840,7 +840,7 @@ async fn handle_queued_job(
|
||||
job_dir: &str,
|
||||
metrics: Metrics,
|
||||
same_worker_tx: Sender<Uuid>,
|
||||
base_internal_url: &str
|
||||
base_internal_url: &str,
|
||||
) -> windmill_common::error::Result<()> {
|
||||
if job.canceled {
|
||||
return Err(Error::JsonErr(canceled_job_to_result(&job)))?;
|
||||
@@ -888,10 +888,10 @@ async fn handle_queued_job(
|
||||
logs.push_str(&format!("job {} on worker {}\n", &job.id, &worker_name));
|
||||
let result = match job.job_kind {
|
||||
JobKind::Dependencies => {
|
||||
handle_dependency_job(&job, &mut logs, job_dir, db).await
|
||||
handle_dependency_job(&job, &mut logs, job_dir, db, worker_name).await
|
||||
}
|
||||
JobKind::FlowDependencies => {
|
||||
handle_flow_dependency_job(&job, &mut logs, job_dir, db)
|
||||
handle_flow_dependency_job(&job, &mut logs, job_dir, db, worker_name)
|
||||
.await
|
||||
.map(|()| Value::Null)
|
||||
}
|
||||
@@ -912,7 +912,8 @@ async fn handle_queued_job(
|
||||
job_dir,
|
||||
worker_dir,
|
||||
&mut logs,
|
||||
base_internal_url
|
||||
base_internal_url,
|
||||
worker_name
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -1064,7 +1065,9 @@ async fn handle_code_execution_job(
|
||||
job_dir: &str,
|
||||
worker_dir: &str,
|
||||
logs: &mut String,
|
||||
base_internal_url: &str
|
||||
base_internal_url: &str,
|
||||
worker_name: &str
|
||||
|
||||
) -> error::Result<serde_json::Value> {
|
||||
let (inner_content, requirements_o, language) = match job.job_kind {
|
||||
JobKind::Preview | JobKind::Script_Hub => (
|
||||
@@ -1086,7 +1089,6 @@ async fn handle_code_execution_job(
|
||||
"handle_code_execution_job should never be reachable with a non-code execution job"
|
||||
),
|
||||
};
|
||||
let worker_name = worker_dir.split("/").last().unwrap_or("unknown");
|
||||
let lang_str = job
|
||||
.language
|
||||
.as_ref()
|
||||
@@ -1136,7 +1138,7 @@ mount {{
|
||||
token,
|
||||
&inner_content,
|
||||
&shared_mount,
|
||||
base_internal_url
|
||||
base_internal_url,
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -1151,7 +1153,8 @@ mount {{
|
||||
&inner_content,
|
||||
&shared_mount,
|
||||
requirements_o,
|
||||
base_internal_url
|
||||
base_internal_url,
|
||||
worker_name
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -1166,7 +1169,8 @@ mount {{
|
||||
job_dir,
|
||||
requirements_o,
|
||||
&shared_mount,
|
||||
base_internal_url
|
||||
base_internal_url,
|
||||
worker_name
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -1179,7 +1183,8 @@ mount {{
|
||||
&inner_content,
|
||||
job_dir,
|
||||
&shared_mount,
|
||||
base_internal_url
|
||||
base_internal_url,
|
||||
worker_name
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -1208,6 +1213,7 @@ async fn handle_go_job(
|
||||
requirements_o: Option<String>,
|
||||
shared_mount: &str,
|
||||
base_internal_url: &str,
|
||||
worker_name: &str,
|
||||
) -> Result<serde_json::Value, Error> {
|
||||
//go does not like executing modules at temp root
|
||||
let job_dir = &format!("{job_dir}/go");
|
||||
@@ -1236,6 +1242,7 @@ async fn handle_go_job(
|
||||
db,
|
||||
true,
|
||||
skip_go_mod,
|
||||
worker_name
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -1353,7 +1360,7 @@ func Run(req Req) (interface{{}}, error){{
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()?;
|
||||
handle_child(&job.id, db, logs, build_go, false).await?;
|
||||
handle_child(&job.id, db, logs, build_go, false, worker_name).await?;
|
||||
|
||||
Command::new(NSJAIL_PATH.as_str())
|
||||
.current_dir(job_dir)
|
||||
@@ -1379,7 +1386,7 @@ func Run(req Req) (interface{{}}, error){{
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()?
|
||||
};
|
||||
handle_child(&job.id, db, logs, child, !*DISABLE_NSJAIL).await?;
|
||||
handle_child(&job.id, db, logs, child, !*DISABLE_NSJAIL, worker_name).await?;
|
||||
read_result(job_dir).await
|
||||
}
|
||||
|
||||
@@ -1393,6 +1400,7 @@ async fn handle_bash_job(
|
||||
job_dir: &str,
|
||||
shared_mount: &str,
|
||||
base_internal_url: &str,
|
||||
worker_name: &str,
|
||||
) -> Result<serde_json::Value, Error> {
|
||||
logs.push_str("\n\n--- BASH CODE EXECUTION ---\n");
|
||||
set_logs(logs, &job.id, db).await;
|
||||
@@ -1456,7 +1464,7 @@ async fn handle_bash_job(
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()?
|
||||
};
|
||||
handle_child(&job.id, db, logs, child, !*DISABLE_NSJAIL).await?;
|
||||
handle_child(&job.id, db, logs, child, !*DISABLE_NSJAIL, worker_name).await?;
|
||||
//for now bash jobs have an empty result object
|
||||
Ok(serde_json::json!(logs
|
||||
.lines()
|
||||
@@ -1484,7 +1492,8 @@ async fn handle_deno_job(
|
||||
inner_content: &String,
|
||||
shared_mount: &str,
|
||||
lockfile: Option<String>,
|
||||
base_internal_url: &str
|
||||
base_internal_url: &str,
|
||||
worker_name: &str
|
||||
) -> error::Result<serde_json::Value> {
|
||||
logs.push_str("\n\n--- DENO CODE EXECUTION ---\n");
|
||||
set_logs(logs, &job.id, db).await;
|
||||
@@ -1619,7 +1628,7 @@ run().catch(async (e) => {{
|
||||
}
|
||||
.instrument(trace_span!("create_deno_jail"))
|
||||
.await?;
|
||||
handle_child(&job.id, db, logs, child, !*DISABLE_NSJAIL).await?;
|
||||
handle_child(&job.id, db, logs, child, !*DISABLE_NSJAIL, worker_name).await?;
|
||||
read_result(job_dir).await
|
||||
}
|
||||
|
||||
@@ -1657,7 +1666,7 @@ async fn handle_python_job(
|
||||
token: String,
|
||||
inner_content: &String,
|
||||
shared_mount: &str,
|
||||
base_internal_url: &str
|
||||
base_internal_url: &str,
|
||||
) -> error::Result<serde_json::Value> {
|
||||
create_dependencies_dir(job_dir).await;
|
||||
|
||||
@@ -1671,7 +1680,7 @@ async fn handle_python_job(
|
||||
if requirements.is_empty() {
|
||||
"".to_string()
|
||||
} else {
|
||||
pip_compile(&job.id, &requirements, logs, job_dir, db)
|
||||
pip_compile(&job.id, &requirements, logs, job_dir, db, worker_name)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
Error::ExecutionErr(format!("pip compile failed: {}", e.to_string()))
|
||||
@@ -1888,7 +1897,7 @@ mount {{
|
||||
.spawn()?
|
||||
};
|
||||
|
||||
handle_child(&job.id, db, logs, child, !*DISABLE_NSJAIL).await?;
|
||||
handle_child(&job.id, db, logs, child, !*DISABLE_NSJAIL, worker_name).await?;
|
||||
read_result(job_dir).await
|
||||
}
|
||||
|
||||
@@ -1918,6 +1927,7 @@ async fn handle_dependency_job(
|
||||
logs: &mut String,
|
||||
job_dir: &str,
|
||||
db: &sqlx::Pool<sqlx::Postgres>,
|
||||
worker_name: &str,
|
||||
) -> error::Result<serde_json::Value> {
|
||||
let content = capture_dependency_job(
|
||||
&job.id,
|
||||
@@ -1932,7 +1942,8 @@ async fn handle_dependency_job(
|
||||
.unwrap_or_else(|| "no raw code"),
|
||||
logs,
|
||||
job_dir,
|
||||
db
|
||||
db,
|
||||
worker_name
|
||||
)
|
||||
.await;
|
||||
match content {
|
||||
@@ -1967,6 +1978,7 @@ async fn handle_flow_dependency_job(
|
||||
logs: &mut String,
|
||||
job_dir: &str,
|
||||
db: &sqlx::Pool<sqlx::Postgres>,
|
||||
worker_name: &str,
|
||||
) -> error::Result<()> {
|
||||
let path = job.script_path.clone().ok_or_else(|| {
|
||||
error::Error::InternalErr(
|
||||
@@ -1997,6 +2009,7 @@ async fn handle_flow_dependency_job(
|
||||
logs,
|
||||
job_dir,
|
||||
db,
|
||||
worker_name
|
||||
)
|
||||
.await;
|
||||
match new_lock {
|
||||
@@ -2111,12 +2124,13 @@ async fn capture_dependency_job(
|
||||
job_raw_code: &str,
|
||||
logs: &mut String,
|
||||
job_dir: &str,
|
||||
db: &sqlx::Pool<sqlx::Postgres>
|
||||
db: &sqlx::Pool<sqlx::Postgres>,
|
||||
worker_name: &str
|
||||
) -> error::Result<String> {
|
||||
match job_language {
|
||||
ScriptLang::Python3 => {
|
||||
create_dependencies_dir(job_dir).await;
|
||||
pip_compile(job_id, job_raw_code, logs, job_dir, db ).await
|
||||
pip_compile(job_id, job_raw_code, logs, job_dir, db, worker_name).await
|
||||
}
|
||||
ScriptLang::Go => {
|
||||
install_go_dependencies(
|
||||
@@ -2127,6 +2141,7 @@ async fn capture_dependency_job(
|
||||
db,
|
||||
false,
|
||||
false,
|
||||
worker_name
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -2144,6 +2159,7 @@ async fn pip_compile(
|
||||
logs: &mut String,
|
||||
job_dir: &str,
|
||||
db: &Pool<Postgres>,
|
||||
worker_name: &str
|
||||
) -> error::Result<String> {
|
||||
logs.push_str(&format!("\nresolving dependencies..."));
|
||||
set_logs(logs, job_id, db).await;
|
||||
@@ -2176,7 +2192,7 @@ async fn pip_compile(
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()?;
|
||||
handle_child(job_id, db, logs, child, false)
|
||||
handle_child(job_id, db, logs, child, false, worker_name)
|
||||
.await
|
||||
.map_err(|e| Error::ExecutionErr(format!("Lock file generation failed: {e:?}")))?;
|
||||
let path_lock = format!("{job_dir}/requirements.txt");
|
||||
@@ -2199,6 +2215,7 @@ async fn install_go_dependencies(
|
||||
db: &sqlx::Pool<sqlx::Postgres>,
|
||||
preview: bool,
|
||||
skip_go_mod: bool,
|
||||
worker_name: &str
|
||||
) -> error::Result<String> {
|
||||
if !skip_go_mod {
|
||||
gen_go_mymod(code, job_dir).await?;
|
||||
@@ -2209,7 +2226,7 @@ async fn install_go_dependencies(
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()?;
|
||||
|
||||
handle_child(job_id, db, logs, child, false).await?;
|
||||
handle_child(job_id, db, logs, child, false, worker_name).await?;
|
||||
}
|
||||
let child = Command::new(GO_PATH.as_str())
|
||||
.current_dir(job_dir)
|
||||
@@ -2217,7 +2234,7 @@ async fn install_go_dependencies(
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()?;
|
||||
handle_child(job_id, db, logs, child, false)
|
||||
handle_child(job_id, db, logs, child, false, worker_name)
|
||||
.await
|
||||
.map_err(|e| Error::ExecutionErr(format!("Lock file generation failed: {e:?}")))?;
|
||||
|
||||
@@ -2323,6 +2340,7 @@ async fn handle_child(
|
||||
logs: &mut String,
|
||||
mut child: Child,
|
||||
nsjail: bool,
|
||||
worker_name: &str,
|
||||
) -> error::Result<()> {
|
||||
let update_job_interval = Duration::from_millis(500);
|
||||
let write_logs_delay = Duration::from_millis(500);
|
||||
@@ -2336,11 +2354,14 @@ async fn handle_child(
|
||||
tracing::info!("could not get child pid");
|
||||
}
|
||||
let (set_too_many_logs, mut too_many_logs) = watch::channel::<bool>(false);
|
||||
let (tx, mut rx) = broadcast::channel::<()>(3);
|
||||
let mut rx2 = tx.subscribe();
|
||||
|
||||
|
||||
let output = child_joined_output_stream(&mut child);
|
||||
|
||||
let job_id = job_id.clone();
|
||||
|
||||
let (tx, mut rx) = mpsc::channel::<()>(1);
|
||||
|
||||
/* the cancellation future is polled on by `wait_on_child` while
|
||||
* waiting for the child to exit normally */
|
||||
@@ -2350,10 +2371,22 @@ async fn handle_child(
|
||||
let mut interval = interval(update_job_interval);
|
||||
interval.set_missed_tick_behavior(MissedTickBehavior::Skip);
|
||||
|
||||
let mut i = 1;
|
||||
loop {
|
||||
tokio::select!(
|
||||
_ = rx.recv() => break,
|
||||
_ = interval.tick() => {
|
||||
// update the last_ping column every 5 seconds
|
||||
i+=1;
|
||||
if i % 10 == 0 {
|
||||
sqlx::query!(
|
||||
"UPDATE worker_ping SET ping_at = now() WHERE worker = $1",
|
||||
&worker_name
|
||||
)
|
||||
.execute(&db)
|
||||
.await
|
||||
.expect("update worker ping");
|
||||
}
|
||||
let mem_peak = get_mem_peak(pid, nsjail).await;
|
||||
tracing::info!("{job_id} still running. mem peak: {}kB", mem_peak);
|
||||
let mem_peak = if mem_peak > 0 { Some(mem_peak) } else { None };
|
||||
@@ -2387,10 +2420,10 @@ async fn handle_child(
|
||||
biased;
|
||||
result = child.wait() => return result.map(Ok),
|
||||
Ok(()) = too_many_logs.changed() => KillReason::TooManyLogs,
|
||||
_ = update_job => KillReason::Cancelled,
|
||||
_ = sleep(*TIMEOUT_DURATION) => KillReason::Timeout,
|
||||
_ = update_job => KillReason::Cancelled,
|
||||
};
|
||||
tx.send(()).await.expect("rx should never be dropped");
|
||||
tx.send(()).expect("rx should never be dropped");
|
||||
drop(tx);
|
||||
|
||||
let set_reason = async {
|
||||
@@ -2413,7 +2446,7 @@ async fn handle_child(
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* send SIGKILL and reap child process */
|
||||
let (_, kill) = future::join(set_reason, child.kill()).await;
|
||||
kill.map(|()| Err(kill_reason))
|
||||
@@ -2429,12 +2462,13 @@ async fn handle_child(
|
||||
/* log_remaining is zero when output limit was reached */
|
||||
let mut log_remaining = max_log_size.saturating_sub(logs.chars().count());
|
||||
let mut result = io::Result::Ok(());
|
||||
let mut output = output;
|
||||
let mut output = output.take_until(rx2.recv()).boxed();
|
||||
/* `do_write` resolves the task, but does not contain the Result.
|
||||
* It's useful to know if the task completed. */
|
||||
let (mut do_write, mut write_result) = tokio::spawn(ready(())).remote_handle();
|
||||
|
||||
while let Some(line) = output.by_ref().next().await {
|
||||
while let Some(line) = output.by_ref().next().await {
|
||||
|
||||
let do_write_ = do_write.shared();
|
||||
|
||||
let mut read_lines = stream::once(async { line })
|
||||
@@ -2449,11 +2483,14 @@ async fn handle_child(
|
||||
let mut joined = String::new();
|
||||
|
||||
while let Some(line) = read_lines.next().await {
|
||||
|
||||
match line {
|
||||
Ok(_) if log_remaining == 0 => (),
|
||||
Ok(line) => {
|
||||
if line.is_empty() {
|
||||
continue;
|
||||
}
|
||||
append_with_limit(&mut joined, &line, &mut log_remaining);
|
||||
|
||||
if log_remaining == 0 {
|
||||
tracing::info!(%job_id, "Too many logs lines for job {job_id}");
|
||||
let _ = set_too_many_logs.send(true);
|
||||
@@ -2473,6 +2510,7 @@ async fn handle_child(
|
||||
|
||||
logs.push_str(&joined);
|
||||
|
||||
|
||||
/* Ensure the last flush completed before starting a new one.
|
||||
*
|
||||
* This shouldn't pause since `take_until()` reads lines until `do_write`
|
||||
@@ -2489,8 +2527,7 @@ async fn handle_child(
|
||||
panic::resume_unwind(p);
|
||||
}
|
||||
|
||||
(do_write, write_result) =
|
||||
tokio::spawn(append_logs(job_id, joined, db.clone())).remote_handle();
|
||||
(do_write, write_result) = tokio::spawn(append_logs(job_id, joined, db.clone())).remote_handle();
|
||||
|
||||
if let Err(err) = result {
|
||||
tracing::error!(%job_id, %err, "error reading output for job {job_id}: {err}");
|
||||
@@ -2500,6 +2537,7 @@ async fn handle_child(
|
||||
if *set_too_many_logs.borrow() {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* drop our end of the pipe */
|
||||
@@ -2813,7 +2851,7 @@ async fn handle_python_reqs(
|
||||
.spawn()?
|
||||
};
|
||||
|
||||
let child = handle_child(&job.id, db, logs, child, false).await;
|
||||
let child = handle_child(&job.id, db, logs, child, false, worker_name).await;
|
||||
tracing::info!(
|
||||
worker_name = %worker_name,
|
||||
job_id = %job.id,
|
||||
|
||||
@@ -13,7 +13,7 @@ import sync from "./sync.ts";
|
||||
import { tryResolveVersion } from "./context.ts";
|
||||
import { GlobalOptions } from "./types.ts";
|
||||
|
||||
const VERSION = "v1.72.0";
|
||||
const VERSION = "v1.74.2";
|
||||
|
||||
let command: any = new Command()
|
||||
.name("wmill")
|
||||
|
||||
@@ -139,6 +139,8 @@ async function list(opts: GlobalOptions) {
|
||||
}),
|
||||
)
|
||||
.render();
|
||||
|
||||
console.log('Active: ' + colors.green.bold(activeName || 'none'))
|
||||
}
|
||||
|
||||
async function switchC(opts: GlobalOptions, workspaceName: string) {
|
||||
@@ -313,6 +315,8 @@ async function remove(_opts: GlobalOptions, name: string) {
|
||||
async function whoami(_opts: GlobalOptions) {
|
||||
await requireLogin(_opts)
|
||||
console.log(await UserService.globalWhoami())
|
||||
const activeName = await getActiveWorkspaceName(_opts);
|
||||
console.log('Active: ' + colors.green.bold(activeName || 'none'))
|
||||
}
|
||||
|
||||
const command = new Command()
|
||||
|
||||
65
frontend/package-lock.json
generated
65
frontend/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "windmill",
|
||||
"version": "1.72.0",
|
||||
"version": "1.74.2",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "windmill",
|
||||
"version": "1.72.0",
|
||||
"version": "1.74.2",
|
||||
"dependencies": {
|
||||
"@fortawesome/free-brands-svg-icons": "^6.2.1",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.2.1",
|
||||
@@ -64,10 +64,12 @@
|
||||
"postcss-load-config": "^4.0.1",
|
||||
"prettier": "^2.8.3",
|
||||
"prettier-plugin-svelte": "^2.9.0",
|
||||
"shepherd.js": "^10.0.1",
|
||||
"simple-svelte-autocomplete": "^2.5.1",
|
||||
"stylelint-config-recommended": "^9.0.0",
|
||||
"svelte": "^3.55.1",
|
||||
"svelte-awesome": "^3.0.0",
|
||||
"svelte-awesome-color-picker": "^2.4.1",
|
||||
"svelte-check": "^3.0.2",
|
||||
"svelte-highlight": "^6.2.1",
|
||||
"svelte-overlay": "^1.4.1",
|
||||
@@ -6652,6 +6654,24 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/shepherd.js": {
|
||||
"version": "10.0.1",
|
||||
"resolved": "https://registry.npmjs.org/shepherd.js/-/shepherd.js-10.0.1.tgz",
|
||||
"integrity": "sha512-R32v4b4b0N1gK/vxvRtdhty+SBZlBPcPTSYCwcaAQvFd0n6Xki7cRH6Sx0oD9WB/HCkqCNM1msyIyylXmq635w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@popperjs/core": "^2.11.5",
|
||||
"deepmerge": "^4.2.2",
|
||||
"smoothscroll-polyfill": "^0.4.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "12.* || 14.* || >= 16"
|
||||
},
|
||||
"funding": {
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/rwwagner90"
|
||||
}
|
||||
},
|
||||
"node_modules/signal-exit": {
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
||||
@@ -6738,6 +6758,12 @@
|
||||
"url": "https://github.com/chalk/slice-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/smoothscroll-polyfill": {
|
||||
"version": "0.4.4",
|
||||
"resolved": "https://registry.npmjs.org/smoothscroll-polyfill/-/smoothscroll-polyfill-0.4.4.tgz",
|
||||
"integrity": "sha512-TK5ZA9U5RqCwMpfoMq/l1mrH0JAR7y7KRvOBx0n2869aLxch+gT9GhN3yUfjiw+d/DiF1mKo14+hd62JyMmoBg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/sorcery": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/sorcery/-/sorcery-0.11.0.tgz",
|
||||
@@ -7128,6 +7154,15 @@
|
||||
"svelte": "^3.43.1"
|
||||
}
|
||||
},
|
||||
"node_modules/svelte-awesome-color-picker": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/svelte-awesome-color-picker/-/svelte-awesome-color-picker-2.4.1.tgz",
|
||||
"integrity": "sha512-IzICGgaMRMgyK4uukm27UdTFfN7s0QN3eoFyIGiXXY8FCPumS9T+vffVG4rNhiuUUP4ga5SoGUE+KXuNj5dsrw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"colord": "^2.9.3"
|
||||
}
|
||||
},
|
||||
"node_modules/svelte-chartjs": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/svelte-chartjs/-/svelte-chartjs-3.1.0.tgz",
|
||||
@@ -12979,6 +13014,17 @@
|
||||
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
||||
"dev": true
|
||||
},
|
||||
"shepherd.js": {
|
||||
"version": "10.0.1",
|
||||
"resolved": "https://registry.npmjs.org/shepherd.js/-/shepherd.js-10.0.1.tgz",
|
||||
"integrity": "sha512-R32v4b4b0N1gK/vxvRtdhty+SBZlBPcPTSYCwcaAQvFd0n6Xki7cRH6Sx0oD9WB/HCkqCNM1msyIyylXmq635w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@popperjs/core": "^2.11.5",
|
||||
"deepmerge": "^4.2.2",
|
||||
"smoothscroll-polyfill": "^0.4.4"
|
||||
}
|
||||
},
|
||||
"signal-exit": {
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
||||
@@ -13039,6 +13085,12 @@
|
||||
"is-fullwidth-code-point": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"smoothscroll-polyfill": {
|
||||
"version": "0.4.4",
|
||||
"resolved": "https://registry.npmjs.org/smoothscroll-polyfill/-/smoothscroll-polyfill-0.4.4.tgz",
|
||||
"integrity": "sha512-TK5ZA9U5RqCwMpfoMq/l1mrH0JAR7y7KRvOBx0n2869aLxch+gT9GhN3yUfjiw+d/DiF1mKo14+hd62JyMmoBg==",
|
||||
"dev": true
|
||||
},
|
||||
"sorcery": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/sorcery/-/sorcery-0.11.0.tgz",
|
||||
@@ -13338,6 +13390,15 @@
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"svelte-awesome-color-picker": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/svelte-awesome-color-picker/-/svelte-awesome-color-picker-2.4.1.tgz",
|
||||
"integrity": "sha512-IzICGgaMRMgyK4uukm27UdTFfN7s0QN3eoFyIGiXXY8FCPumS9T+vffVG4rNhiuUUP4ga5SoGUE+KXuNj5dsrw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"colord": "^2.9.3"
|
||||
}
|
||||
},
|
||||
"svelte-chartjs": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/svelte-chartjs/-/svelte-chartjs-3.1.0.tgz",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "windmill",
|
||||
"version": "1.72.0",
|
||||
"version": "1.74.2",
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
@@ -43,10 +43,12 @@
|
||||
"postcss-load-config": "^4.0.1",
|
||||
"prettier": "^2.8.3",
|
||||
"prettier-plugin-svelte": "^2.9.0",
|
||||
"shepherd.js": "^10.0.1",
|
||||
"simple-svelte-autocomplete": "^2.5.1",
|
||||
"stylelint-config-recommended": "^9.0.0",
|
||||
"svelte": "^3.55.1",
|
||||
"svelte-awesome": "^3.0.0",
|
||||
"svelte-awesome-color-picker": "^2.4.1",
|
||||
"svelte-check": "^3.0.2",
|
||||
"svelte-highlight": "^6.2.1",
|
||||
"svelte-overlay": "^1.4.1",
|
||||
|
||||
@@ -232,8 +232,14 @@
|
||||
<Badge color={validCode ? 'green' : 'red'} class="min-w-[60px] mr-3">
|
||||
{validCode ? 'Valid' : 'Invalid'}
|
||||
</Badge>
|
||||
<div class="flex items-center divide-x">
|
||||
<Popover notClickable placement="bottom" disapperTimoout={0} class="pr-1" disablePopup={!iconOnly}>
|
||||
<div id="script-tutorial-3" class="flex items-center divide-x">
|
||||
<Popover
|
||||
notClickable
|
||||
placement="bottom"
|
||||
disapperTimoout={0}
|
||||
class="pr-1"
|
||||
disablePopup={!iconOnly}
|
||||
>
|
||||
<Button
|
||||
color="light"
|
||||
btnClasses="!font-medium !h-full"
|
||||
@@ -245,11 +251,15 @@
|
||||
>
|
||||
+Context Var
|
||||
</Button>
|
||||
<svelte:fragment slot="text">
|
||||
Add context variable
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="text">Add context variable</svelte:fragment>
|
||||
</Popover>
|
||||
<Popover notClickable placement="bottom" disapperTimoout={0} class="px-1" disablePopup={!iconOnly}>
|
||||
<Popover
|
||||
notClickable
|
||||
placement="bottom"
|
||||
disapperTimoout={0}
|
||||
class="px-1"
|
||||
disablePopup={!iconOnly}
|
||||
>
|
||||
<Button
|
||||
color="light"
|
||||
btnClasses="!font-medium !h-full"
|
||||
@@ -261,11 +271,15 @@
|
||||
>
|
||||
+Variable
|
||||
</Button>
|
||||
<svelte:fragment slot="text">
|
||||
Add variable
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="text">Add variable</svelte:fragment>
|
||||
</Popover>
|
||||
<Popover notClickable placement="bottom" disapperTimoout={0} class="px-1" disablePopup={!iconOnly}>
|
||||
<Popover
|
||||
notClickable
|
||||
placement="bottom"
|
||||
disapperTimoout={0}
|
||||
class="px-1"
|
||||
disablePopup={!iconOnly}
|
||||
>
|
||||
<Button
|
||||
btnClasses="!font-medium !h-full"
|
||||
size="xs"
|
||||
@@ -277,11 +291,15 @@
|
||||
>
|
||||
+Resource
|
||||
</Button>
|
||||
<svelte:fragment slot="text">
|
||||
Add resource
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="text">Add resource</svelte:fragment>
|
||||
</Popover>
|
||||
<Popover notClickable placement="bottom" disapperTimoout={0} class="px-1" disablePopup={!iconOnly}>
|
||||
<Popover
|
||||
notClickable
|
||||
placement="bottom"
|
||||
disapperTimoout={0}
|
||||
class="px-1"
|
||||
disablePopup={!iconOnly}
|
||||
>
|
||||
<Button
|
||||
btnClasses="!font-medium !h-full"
|
||||
size="xs"
|
||||
@@ -293,11 +311,15 @@
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
<svelte:fragment slot="text">
|
||||
Reset
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="text">Reset</svelte:fragment>
|
||||
</Popover>
|
||||
<Popover notClickable placement="bottom" disapperTimoout={0} class="px-1" disablePopup={!iconOnly}>
|
||||
<Popover
|
||||
notClickable
|
||||
placement="bottom"
|
||||
disapperTimoout={0}
|
||||
class="px-1"
|
||||
disablePopup={!iconOnly}
|
||||
>
|
||||
<Button
|
||||
btnClasses="!font-medium !h-full"
|
||||
size="xs"
|
||||
@@ -320,11 +342,15 @@
|
||||
{/if}
|
||||
</span>
|
||||
</Button>
|
||||
<svelte:fragment slot="text">
|
||||
Reload assistant
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="text">Reload assistant</svelte:fragment>
|
||||
</Popover>
|
||||
<Popover notClickable placement="bottom" disapperTimoout={0} class="px-1" disablePopup={!iconOnly}>
|
||||
<Popover
|
||||
notClickable
|
||||
placement="bottom"
|
||||
disapperTimoout={0}
|
||||
class="px-1"
|
||||
disablePopup={!iconOnly}
|
||||
>
|
||||
<Button
|
||||
btnClasses="!font-medium"
|
||||
size="xs"
|
||||
@@ -342,7 +368,13 @@
|
||||
</Popover>
|
||||
</div>
|
||||
</div>
|
||||
<Popover notClickable placement="bottom" disapperTimoout={0} class="px-1" disablePopup={!iconOnly}>
|
||||
<Popover
|
||||
notClickable
|
||||
placement="bottom"
|
||||
disapperTimoout={0}
|
||||
class="px-1"
|
||||
disablePopup={!iconOnly}
|
||||
>
|
||||
<Button
|
||||
btnClasses="!font-medium"
|
||||
size="xs"
|
||||
@@ -354,9 +386,7 @@
|
||||
>
|
||||
Script
|
||||
</Button>
|
||||
<svelte:fragment slot="text">
|
||||
Script
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="text">Script</svelte:fragment>
|
||||
</Popover>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -4,23 +4,23 @@
|
||||
import { initHistory, redo, undo } from '$lib/history'
|
||||
import { userStore, workspaceStore } from '$lib/stores'
|
||||
import { encodeState, formatCron, loadHubScripts, sendUserToast } from '$lib/utils'
|
||||
import { faCalendarAlt, faEye, faPen, faSave } from '@fortawesome/free-solid-svg-icons'
|
||||
import { Redo, Undo } from 'lucide-svelte'
|
||||
import { faCalendarAlt, faPen, faSave } from '@fortawesome/free-solid-svg-icons'
|
||||
import { setContext } from 'svelte'
|
||||
import { writable, type Writable } from 'svelte/store'
|
||||
import CenteredPage from './CenteredPage.svelte'
|
||||
import { Button, ButtonPopup, ButtonPopupItem } from './common'
|
||||
import { Button, ButtonPopup, ButtonPopupItem, UndoRedo } from './common'
|
||||
import { dirtyStore } from './common/confirmationModal/dirtyStore'
|
||||
import UnsavedConfirmationModal from './common/confirmationModal/UnsavedConfirmationModal.svelte'
|
||||
import { OFFSET } from './CronInput.svelte'
|
||||
import ScriptEditorDrawer from './flows/content/ScriptEditorDrawer.svelte'
|
||||
import FlowEditor from './flows/FlowEditor.svelte'
|
||||
import type { FlowState } from './flows/flowState'
|
||||
import { dfs } from './flows/flowStore'
|
||||
import FlowImportExportMenu from './flows/header/FlowImportExportMenu.svelte'
|
||||
import FlowPreviewButtons from './flows/header/FlowPreviewButtons.svelte'
|
||||
import { loadFlowSchedule, type Schedule } from './flows/scheduleUtils'
|
||||
import type { FlowEditorContext } from './flows/types'
|
||||
import { cleanInputs } from './flows/utils'
|
||||
import { Tour } from './tutorial'
|
||||
|
||||
export let initialPath: string = ''
|
||||
export let selectedId: string | undefined
|
||||
@@ -28,6 +28,7 @@
|
||||
export let loading = false
|
||||
export let flowStore: Writable<Flow>
|
||||
export let flowStateStore: Writable<FlowState>
|
||||
export let tour = false
|
||||
|
||||
async function createSchedule(path: string) {
|
||||
const { cron, args, enabled } = $scheduleStore
|
||||
@@ -193,11 +194,66 @@
|
||||
$: initialPath && $workspaceStore && loadSchedule()
|
||||
|
||||
loadHubScripts()
|
||||
|
||||
function onKeyDown(event: KeyboardEvent) {
|
||||
switch (event.key) {
|
||||
case 'Z':
|
||||
if (event.ctrlKey) {
|
||||
$flowStore = redo(history)
|
||||
event.preventDefault()
|
||||
}
|
||||
break
|
||||
case 'z':
|
||||
if (event.ctrlKey) {
|
||||
$flowStore = undo(history, $flowStore)
|
||||
$selectedIdStore = 'Input'
|
||||
event.preventDefault()
|
||||
}
|
||||
break
|
||||
case 's':
|
||||
if (event.ctrlKey) {
|
||||
saveFlow(false)
|
||||
event.preventDefault()
|
||||
}
|
||||
break
|
||||
case 'ArrowDown': {
|
||||
let ids = generateIds()
|
||||
let idx = ids.indexOf($selectedIdStore)
|
||||
if (idx > -1 && idx < ids.length - 1) {
|
||||
$selectedIdStore = ids[idx + 1]
|
||||
event.preventDefault()
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'ArrowUp': {
|
||||
let ids = generateIds()
|
||||
let idx = ids.indexOf($selectedIdStore)
|
||||
if (idx > 0 && idx < ids.length) {
|
||||
$selectedIdStore = ids[idx - 1]
|
||||
event.preventDefault()
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function generateIds() {
|
||||
return [
|
||||
'settings-metadata',
|
||||
'constants',
|
||||
...dfs($flowStore.value.modules, (module) => module.id)
|
||||
]
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={onKeyDown} />
|
||||
|
||||
{#if tour}
|
||||
<Tour tutorial="flow" />
|
||||
{/if}
|
||||
|
||||
{#if !$userStore?.operator}
|
||||
<ScriptEditorDrawer bind:this={$scriptEditorDrawer} />
|
||||
<UnsavedConfirmationModal />
|
||||
|
||||
<div class="flex flex-col flex-1 h-screen">
|
||||
<!-- Nav between steps-->
|
||||
@@ -206,32 +262,17 @@
|
||||
>
|
||||
<div class="flex flex-row gap-4 items-center">
|
||||
<FlowImportExportMenu />
|
||||
<div class="flex gap-1">
|
||||
<Button
|
||||
title="Undo"
|
||||
disabled={$history.index == 0}
|
||||
variant="border"
|
||||
color="dark"
|
||||
size="xs"
|
||||
on:click={async () => {
|
||||
$flowStore = undo(history, $flowStore)
|
||||
}}
|
||||
>
|
||||
<Undo size={14} />
|
||||
</Button>
|
||||
<Button
|
||||
title="Redo"
|
||||
disabled={$history.index == $history.history.length - 1}
|
||||
variant="border"
|
||||
color="dark"
|
||||
size="xs"
|
||||
on:click={async () => {
|
||||
$flowStore = redo(history)
|
||||
}}
|
||||
>
|
||||
<Redo size={14} />
|
||||
</Button>
|
||||
</div>
|
||||
<UndoRedo
|
||||
undoProps={{ disabled: $history.index === 0 }}
|
||||
redoProps={{ disabled: $history.index === $history.history.length - 1 }}
|
||||
on:undo={() => {
|
||||
$flowStore = undo(history, $flowStore)
|
||||
$selectedIdStore = 'Input'
|
||||
}}
|
||||
on:redo={() => {
|
||||
$flowStore = redo(history)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="gap-1 flex-row hidden md:flex shrink overflow-hidden">
|
||||
|
||||
@@ -409,8 +409,8 @@
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="flex-row flex justify-between">
|
||||
<div><span class="font-mono text-sm">{path}</span></div>
|
||||
<div class="flex-row flex justify-between w-full">
|
||||
<div><span class="font-mono text-sm break-all">{path}</span></div>
|
||||
<div class="text-red-600 text-2xs">{error}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,17 +11,38 @@
|
||||
import { SELECT_INPUT_DEFAULT_STYLE } from '../defaults'
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
let resources: Resource[] = []
|
||||
|
||||
export let initialValue: string | undefined = undefined
|
||||
export let value: string | undefined = initialValue
|
||||
export let resourceType: string | undefined = undefined
|
||||
|
||||
let valueSelect =
|
||||
initialValue || value
|
||||
? {
|
||||
value: value ?? initialValue,
|
||||
label: value ?? initialValue
|
||||
}
|
||||
: undefined
|
||||
|
||||
let collection = [valueSelect]
|
||||
|
||||
async function loadResources(resourceType: string | undefined) {
|
||||
const v = value
|
||||
resources = await ResourceService.listResource({ workspace: $workspaceStore!, resourceType })
|
||||
value = v
|
||||
const nc = (
|
||||
await ResourceService.listResource({
|
||||
workspace: $workspaceStore!,
|
||||
resourceType
|
||||
})
|
||||
).map((x) => ({
|
||||
value: x.path,
|
||||
label: x.path
|
||||
}))
|
||||
|
||||
if (!nc.find((x) => x.value == value) && (initialValue || value)) {
|
||||
nc.push({ value: value ?? initialValue!, label: value ?? initialValue! })
|
||||
}
|
||||
collection = nc
|
||||
}
|
||||
|
||||
$: {
|
||||
if ($workspaceStore) {
|
||||
loadResources(resourceType)
|
||||
@@ -29,10 +50,6 @@
|
||||
}
|
||||
$: dispatch('change', value)
|
||||
|
||||
$: collection = resources.map((x) => ({
|
||||
value: x.path,
|
||||
label: x.path
|
||||
}))
|
||||
let appConnect: AppConnect
|
||||
let resourceEditor: ResourceEditor
|
||||
</script>
|
||||
@@ -41,6 +58,7 @@
|
||||
on:refresh={async (e) => {
|
||||
await loadResources(resourceType)
|
||||
value = e.detail
|
||||
valueSelect = { value: e.detail, label: e.detail }
|
||||
}}
|
||||
newPageOAuth
|
||||
bind:this={appConnect}
|
||||
@@ -50,18 +68,24 @@
|
||||
bind:this={resourceEditor}
|
||||
on:refresh={async (e) => {
|
||||
await loadResources(resourceType)
|
||||
console.log(e)
|
||||
if (e.detail) {
|
||||
value = e.detail
|
||||
valueSelect = { value: e.detail, label: e.detail }
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<div class="flex flex-row gap-x-1 w-full">
|
||||
<Select
|
||||
listAutoWidth={false}
|
||||
value={collection.find((x) => x.value == value)}
|
||||
bind:justValue={value}
|
||||
value={valueSelect}
|
||||
on:change={(e) => {
|
||||
value = e.detail.value
|
||||
valueSelect = e.detail
|
||||
}}
|
||||
on:clear={() => {
|
||||
value = undefined
|
||||
valueSelect = undefined
|
||||
}}
|
||||
items={collection}
|
||||
class="text-clip grow min-w-0"
|
||||
placeholder="{resourceType} resource"
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
import ScriptEditor from './ScriptEditor.svelte'
|
||||
import ScriptSchema from './ScriptSchema.svelte'
|
||||
import CenteredPage from './CenteredPage.svelte'
|
||||
import UnsavedConfirmationModal from './common/confirmationModal/UnsavedConfirmationModal.svelte'
|
||||
import { dirtyStore } from './common/confirmationModal/dirtyStore'
|
||||
import { Button, ButtonPopup, ButtonPopupItem, Kbd } from './common'
|
||||
import { faChevronDown, faChevronUp, faPen, faSave } from '@fortawesome/free-solid-svg-icons'
|
||||
@@ -121,10 +120,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={onKeyDown} />
|
||||
|
||||
{#if !$userStore?.operator}
|
||||
<UnsavedConfirmationModal />
|
||||
<div class="flex flex-col h-screen">
|
||||
<!-- Nav between steps-->
|
||||
<div class="flex flex-col w-full px-2 py-1 border-b shadow-sm">
|
||||
@@ -350,6 +346,7 @@
|
||||
lang={script.language}
|
||||
{initialArgs}
|
||||
{kind}
|
||||
tour
|
||||
/>
|
||||
{:else if step === 3}
|
||||
<CenteredPage>
|
||||
@@ -360,3 +357,5 @@
|
||||
{:else}
|
||||
Script Builder not available to operators
|
||||
{/if}
|
||||
|
||||
<svelte:window on:keydown={onKeyDown} />
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
import { Button, Kbd } from './common'
|
||||
import SplitPanesWrapper from './splitPanes/SplitPanesWrapper.svelte'
|
||||
import WindmillIcon from './icons/WindmillIcon.svelte'
|
||||
import { Tour } from './tutorial'
|
||||
|
||||
// Exported
|
||||
export let schema: Schema = emptySchema()
|
||||
@@ -27,6 +28,7 @@
|
||||
export let initialArgs: Record<string, any> = {}
|
||||
export let fixedOverflowWidgets = true
|
||||
export let noSyncFromGithub = false
|
||||
export let tour = false
|
||||
|
||||
let websocketAlive = { pyright: false, black: false, deno: false, go: false }
|
||||
|
||||
@@ -114,6 +116,10 @@
|
||||
|
||||
<svelte:window on:keydown={onKeyDown} />
|
||||
|
||||
{#if tour}
|
||||
<Tour tutorial="script" />
|
||||
{/if}
|
||||
|
||||
<div class="border-b-2 shadow-sm px-1 pr-4" bind:clientWidth={width}>
|
||||
<div class="flex justify-between space-x-2">
|
||||
<EditorBar
|
||||
@@ -146,6 +152,7 @@
|
||||
<Splitpanes class="!overflow-visible">
|
||||
<Pane size={60} minSize={10} class="!overflow-visible">
|
||||
<div
|
||||
id="script-tutorial-1"
|
||||
class="pl-2 h-full !overflow-visible"
|
||||
on:mouseleave={() => {
|
||||
inferSchema(code)
|
||||
@@ -180,7 +187,7 @@
|
||||
</Pane>
|
||||
<Pane size={40} minSize={10}>
|
||||
<div class="flex flex-col h-full">
|
||||
<div class="px-2 w-full border-b py-1">
|
||||
<div id="script-tutorial-4" class="px-2 w-full border-b py-1">
|
||||
{#if testIsLoading}
|
||||
<Button on:click={testJobLoader?.cancelJob} btnClasses="w-full" color="red" size="xs">
|
||||
<WindmillIcon
|
||||
@@ -211,7 +218,7 @@
|
||||
</div>
|
||||
<Splitpanes horizontal class="!max-h-[calc(100%-43px)]">
|
||||
<Pane size={33}>
|
||||
<div class="px-2">
|
||||
<div id="script-tutorial-2" class="px-2">
|
||||
<div class="break-words relative font-sans">
|
||||
<SchemaForm compact {schema} bind:args bind:isValid />
|
||||
</div>
|
||||
|
||||
@@ -186,9 +186,9 @@
|
||||
syncIteration++
|
||||
await loadTestJob(id)
|
||||
let nextIteration = 50
|
||||
if (syncIteration == ITERATIONS_BEFORE_SLOW_REFRESH) {
|
||||
if (syncIteration > ITERATIONS_BEFORE_SLOW_REFRESH) {
|
||||
nextIteration = 500
|
||||
} else if (syncIteration == ITERATIONS_BEFORE_SUPER_SLOW_REFRESH) {
|
||||
} else if (syncIteration > ITERATIONS_BEFORE_SUPER_SLOW_REFRESH) {
|
||||
nextIteration = 2000
|
||||
}
|
||||
setTimeout(() => syncer(id), nextIteration)
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
const dispatch = createEventDispatcher()
|
||||
</script>
|
||||
|
||||
<span class="{$$props.class} z-auto">
|
||||
<span class="{$$props.class ?? ''} z-auto">
|
||||
<label
|
||||
for={id}
|
||||
class="inline-flex items-center mt-2 duration-200 {disabled
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import type RunnableComponent from '../helpers/RunnableComponent.svelte'
|
||||
@@ -22,10 +22,11 @@
|
||||
export let noWFull = false
|
||||
export let preclickAction: (() => Promise<void>) | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'button'> | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
export const staticOutputs: string[] = ['loading', 'result']
|
||||
|
||||
const { runnableComponents, worldStore, app } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { runnableComponents, worldStore, app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let labelValue: string
|
||||
let color: ButtonType.Color
|
||||
@@ -141,6 +142,7 @@
|
||||
autoRefresh={false}
|
||||
goto={gotoUrl}
|
||||
{gotoNewTab}
|
||||
{render}
|
||||
>
|
||||
<AlignWrapper {noWFull} {horizontalAlignment} {verticalAlignment}>
|
||||
{#if errorsMessage}
|
||||
@@ -164,11 +166,11 @@
|
||||
{loading}
|
||||
>
|
||||
<span class="truncate inline-flex gap-2 items-center">
|
||||
{#if beforeIconComponent}
|
||||
{#if beforeIcon && beforeIconComponent}
|
||||
<svelte:component this={beforeIconComponent} size={14} />
|
||||
{/if}
|
||||
<div>{labelValue}</div>
|
||||
{#if afterIconComponent}
|
||||
{#if afterIcon && afterIconComponent}
|
||||
<svelte:component this={afterIconComponent} size={14} />
|
||||
{/if}
|
||||
</span>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import { Icon } from 'svelte-awesome'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
@@ -19,11 +19,12 @@
|
||||
export let extraQueryParams: Record<string, any> = {}
|
||||
export let horizontalAlignment: 'left' | 'center' | 'right' | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'container' | 'button'> | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
export const staticOutputs: string[] = ['loading', 'result']
|
||||
|
||||
const { app, runnableComponents, worldStore, stateId } =
|
||||
getContext<AppEditorContext>('AppEditorContext')
|
||||
getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let labelValue: string = 'Default label'
|
||||
let color: ButtonType.Color
|
||||
@@ -61,6 +62,7 @@
|
||||
<InputValue {id} input={configuration.size} bind:value={size} />
|
||||
|
||||
<RunnableWrapper
|
||||
{render}
|
||||
bind:runnableComponent
|
||||
{componentInput}
|
||||
{id}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import { Icon } from 'svelte-awesome'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import type RunnableComponent from '../helpers/RunnableComponent.svelte'
|
||||
@@ -22,10 +22,11 @@
|
||||
export let horizontalAlignment: 'left' | 'center' | 'right' | undefined = undefined
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'button' | 'popup'> | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
export const staticOutputs: string[] = ['loading', 'result']
|
||||
|
||||
const { app, runnableComponents, worldStore } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { app, runnableComponents, worldStore } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let labelValue: string = 'Default label'
|
||||
let color: ButtonType.Color
|
||||
@@ -93,6 +94,7 @@
|
||||
}}
|
||||
>
|
||||
<RunnableWrapper
|
||||
{render}
|
||||
bind:runnableComponent
|
||||
{componentInput}
|
||||
{id}
|
||||
|
||||
@@ -17,16 +17,17 @@
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
|
||||
export let id: string
|
||||
export let componentInput: AppInput | undefined
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let initializing: boolean | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
export const staticOutputs: string[] = ['loading', 'result']
|
||||
const { app } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
ChartJS.register(
|
||||
Title,
|
||||
@@ -89,7 +90,7 @@
|
||||
<InputValue {id} input={configuration.theme} bind:value={theme} />
|
||||
<InputValue {id} input={configuration.line} bind:value={lineChart} />
|
||||
|
||||
<RunnableWrapper flexWrap autoRefresh {componentInput} {id} bind:initializing bind:result>
|
||||
<RunnableWrapper {render} flexWrap autoRefresh {componentInput} {id} bind:initializing bind:result>
|
||||
<div class="w-full h-full {css?.container?.class ?? ''}" style={css?.container?.style ?? ''}>
|
||||
{#if result}
|
||||
{#if lineChart}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import type { AppInput } from '../../inputType'
|
||||
import {
|
||||
IS_APP_PUBLIC_CONTEXT_KEY,
|
||||
type AppEditorContext,
|
||||
type AppViewerContext,
|
||||
type ComponentCustomCSS
|
||||
} from '../../types'
|
||||
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
|
||||
@@ -14,15 +14,16 @@
|
||||
export let componentInput: AppInput | undefined
|
||||
export let initializing: boolean | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'header' | 'container'> | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
const requireHtmlApproval = getContext<boolean | undefined>(IS_APP_PUBLIC_CONTEXT_KEY)
|
||||
const { app } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
let result: any = undefined
|
||||
|
||||
export const staticOutputs: string[] = ['result', 'loading']
|
||||
</script>
|
||||
|
||||
<RunnableWrapper flexWrap {componentInput} {id} bind:initializing bind:result>
|
||||
<RunnableWrapper {render} flexWrap {componentInput} {id} bind:initializing bind:result>
|
||||
<div
|
||||
class={twMerge(
|
||||
'w-full border-b px-2 text-xs p-1 font-semibold bg-gray-500 text-white rounded-t-sm',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
|
||||
|
||||
@@ -9,9 +9,10 @@
|
||||
export let componentInput: AppInput | undefined
|
||||
export let initializing: boolean | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
export const staticOutputs: string[] = ['result', 'loading']
|
||||
const { app } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let result: string | undefined = undefined
|
||||
let h: number | undefined = undefined
|
||||
@@ -28,7 +29,15 @@
|
||||
bind:clientHeight={h}
|
||||
bind:clientWidth={w}
|
||||
>
|
||||
<RunnableWrapper autoRefresh flexWrap {componentInput} {id} bind:initializing bind:result>
|
||||
<RunnableWrapper
|
||||
{render}
|
||||
autoRefresh
|
||||
flexWrap
|
||||
{componentInput}
|
||||
{id}
|
||||
bind:initializing
|
||||
bind:result
|
||||
>
|
||||
{#key result}
|
||||
<iframe
|
||||
frameborder="0"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import { AlignWrapper, InputValue } from '../helpers'
|
||||
import { loadIcon } from '../icon'
|
||||
@@ -12,8 +12,9 @@
|
||||
export let configuration: Record<string, AppInput>
|
||||
export const staticOutputs: string[] = []
|
||||
export let customCss: ComponentCustomCSS<'container' | 'icon'> | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
const { app } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let icon: string | undefined = undefined
|
||||
let size: number
|
||||
@@ -21,12 +22,10 @@
|
||||
let strokeWidth: number
|
||||
let iconComponent: any
|
||||
|
||||
$: icon && handleIcon()
|
||||
$: handleIcon(icon)
|
||||
|
||||
async function handleIcon() {
|
||||
if (icon) {
|
||||
iconComponent = await loadIcon(icon)
|
||||
}
|
||||
async function handleIcon(i?: string) {
|
||||
iconComponent = i ? await loadIcon(i) : undefined
|
||||
}
|
||||
|
||||
$: css = concatCustomCss($app.css?.iconcomponent, customCss)
|
||||
@@ -38,12 +37,13 @@
|
||||
<InputValue {id} input={configuration.strokeWidth} bind:value={strokeWidth} />
|
||||
|
||||
<AlignWrapper
|
||||
{render}
|
||||
{horizontalAlignment}
|
||||
{verticalAlignment}
|
||||
class={css?.container?.class ?? ''}
|
||||
style={css?.container?.style ?? ''}
|
||||
>
|
||||
{#if iconComponent}
|
||||
{#if icon && iconComponent}
|
||||
<svelte:component
|
||||
this={iconComponent}
|
||||
size={size || 24}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import type { staticValues } from '../../editor/componentsPanel/componentStaticValues'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
|
||||
@@ -13,8 +13,9 @@
|
||||
export let configuration: Record<string, AppInput>
|
||||
export const staticOutputs: string[] = ['loading']
|
||||
export let customCss: ComponentCustomCSS<'image'> | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
const { app } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
const fit: Record<FitOption, string> = {
|
||||
cover: 'object-cover',
|
||||
contain: 'object-contain',
|
||||
@@ -32,10 +33,12 @@
|
||||
<InputValue {id} input={configuration.imageFit} bind:value={imageFit} />
|
||||
<InputValue {id} input={configuration.altText} bind:value={altText} />
|
||||
|
||||
<img
|
||||
on:pointerdown|preventDefault
|
||||
src={source}
|
||||
alt={altText}
|
||||
style={css?.image?.style ?? ''}
|
||||
class={twMerge(`w-full h-full ${fit[imageFit || 'cover']}`, css?.image?.class ?? '')}
|
||||
/>
|
||||
{#if render}
|
||||
<img
|
||||
on:pointerdown|preventDefault
|
||||
src={source}
|
||||
alt={altText}
|
||||
style={css?.image?.style ?? ''}
|
||||
class={twMerge(`w-full h-full ${fit[imageFit || 'cover']}`, css?.image?.class ?? '')}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { getContext, onMount } from 'svelte'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import { InputValue } from '../helpers'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { Map, View, Feature } from 'ol'
|
||||
@@ -32,9 +32,10 @@
|
||||
export let configuration: Record<string, AppInput>
|
||||
export const staticOutputs: string[] = ['mapRegion']
|
||||
export let customCss: ComponentCustomCSS<'map'> | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
const { app, worldStore, selectedComponent, connectingInput, focusedGrid, mode } =
|
||||
getContext<AppEditorContext>('AppEditorContext')
|
||||
getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
$: outputs = $worldStore?.outputsById[id] as {
|
||||
mapRegion: Output<{
|
||||
@@ -192,23 +193,25 @@
|
||||
<InputValue {id} input={configuration.zoom} bind:value={zoom} />
|
||||
<InputValue {id} input={configuration.markers} bind:value={markers} />
|
||||
|
||||
<div class="relative h-full w-full">
|
||||
<div
|
||||
on:pointerdown|stopPropagation={selectComponent}
|
||||
bind:this={mapElement}
|
||||
class={twMerge(`w-full h-full`, css?.map?.class ?? '')}
|
||||
style={css?.map?.style ?? ''}
|
||||
/>
|
||||
|
||||
{#if $mode !== 'preview'}
|
||||
{#if render}
|
||||
<div class="relative h-full w-full">
|
||||
<div
|
||||
class="absolute bottom-0 left-0 px-1 py-0.5 bg-indigo-500 text-white text-2xs"
|
||||
on:pointerdown={handleSyncRegion}
|
||||
>
|
||||
Set region
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
on:pointerdown|stopPropagation={selectComponent}
|
||||
bind:this={mapElement}
|
||||
class={twMerge(`w-full h-full`, css?.map?.class ?? '')}
|
||||
style={css?.map?.style ?? ''}
|
||||
/>
|
||||
|
||||
{#if $mode !== 'preview'}
|
||||
<div
|
||||
class="absolute bottom-0 left-0 px-1 py-0.5 bg-indigo-500 text-white text-2xs"
|
||||
on:pointerdown={handleSyncRegion}
|
||||
>
|
||||
Set region
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style global lang="postcss">
|
||||
.ol-overlaycontainer-stopevent {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import { getDocument, type PDFDocumentProxy, type PDFPageProxy } from 'pdfjs-dist'
|
||||
import 'pdfjs-dist/build/pdf.worker.entry'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import { throttle } from '../../../../utils'
|
||||
@@ -17,8 +17,9 @@
|
||||
export let configuration: Record<string, AppInput>
|
||||
export const staticOutputs: string[] = ['loading']
|
||||
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
const { app, mode, selectedComponent } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { app, mode, selectedComponent } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let source: string | ArrayBuffer | undefined = undefined
|
||||
let wrapper: HTMLDivElement | undefined = undefined
|
||||
@@ -184,135 +185,137 @@
|
||||
<InputValue {id} input={configuration.source} bind:value={source} />
|
||||
<InputValue {id} input={configuration.zoom} bind:value={zoom} />
|
||||
|
||||
<div class="relative w-full h-full bg-gray-100">
|
||||
{#if source && zoom}
|
||||
{#if pages?.length}
|
||||
<div
|
||||
bind:clientWidth={controlsWidth}
|
||||
bind:clientHeight={controlsHeight}
|
||||
class="fixed flex {$mode !== 'preview'
|
||||
? 'w-[calc(100%-2px)] top-[1px]'
|
||||
: 'w-full top-0'} {wideView
|
||||
? 'justify-center gap-14'
|
||||
: '!justify-between'} overflow-x-auto bg-white border mx-auto py-1"
|
||||
>
|
||||
<div class="flex justify-start items-center px-2 text-gray-600 text-sm">
|
||||
<Button
|
||||
on:click={() => zoom && (zoom -= 10)}
|
||||
disabled={!doc}
|
||||
size="xs"
|
||||
color="light"
|
||||
variant="border"
|
||||
title="Zoom out"
|
||||
aria-label="Zoom out"
|
||||
btnClasses="!rounded-r-none !px-2"
|
||||
>
|
||||
<ZoomOut size={16} />
|
||||
</Button>
|
||||
{#if wideView}
|
||||
{#if render}
|
||||
<div class="relative w-full h-full bg-gray-100">
|
||||
{#if source && zoom}
|
||||
{#if pages?.length}
|
||||
<div
|
||||
bind:clientWidth={controlsWidth}
|
||||
bind:clientHeight={controlsHeight}
|
||||
class="fixed flex {$mode !== 'preview'
|
||||
? 'w-[calc(100%-2px)] top-[1px]'
|
||||
: 'w-full top-0'} {wideView
|
||||
? 'justify-center gap-14'
|
||||
: '!justify-between'} overflow-x-auto bg-white border mx-auto py-1"
|
||||
>
|
||||
<div class="flex justify-start items-center px-2 text-gray-600 text-sm">
|
||||
<Button
|
||||
on:click={() => (zoom = 100)}
|
||||
on:click={() => zoom && (zoom -= 10)}
|
||||
disabled={!doc}
|
||||
size="xs"
|
||||
color="light"
|
||||
variant="border"
|
||||
title="Reset zoom"
|
||||
aria-label="Reset zoom"
|
||||
btnClasses="!w-[50px] !font-medium !rounded-none !border-l-0 !px-1"
|
||||
title="Zoom out"
|
||||
aria-label="Zoom out"
|
||||
btnClasses="!rounded-r-none !px-2"
|
||||
>
|
||||
{zoom.toFixed(0)}%
|
||||
<ZoomOut size={16} />
|
||||
</Button>
|
||||
{/if}
|
||||
<Button
|
||||
on:click={() => renderPdf(true, true)}
|
||||
disabled={!doc}
|
||||
size="xs"
|
||||
color="light"
|
||||
variant="border"
|
||||
title="Scale to viewport"
|
||||
aria-label="Scale to viewport"
|
||||
btnClasses="!rounded-none !border-l-0 !px-2"
|
||||
>
|
||||
<MoveHorizontal size={16} />
|
||||
</Button>
|
||||
<Button
|
||||
on:click={() => zoom && (zoom += 10)}
|
||||
disabled={!doc}
|
||||
size="xs"
|
||||
color="light"
|
||||
variant="border"
|
||||
title="Zoom in"
|
||||
aria-label="Zoom in"
|
||||
btnClasses="!rounded-l-none !px-2 !border-l-0"
|
||||
>
|
||||
<ZoomIn size={16} />
|
||||
</Button>
|
||||
</div>
|
||||
<div class="center-center px-2 text-gray-600 text-sm">
|
||||
<input
|
||||
on:input={({ currentTarget }) => {
|
||||
scrollToPage(currentTarget.valueAsNumber)
|
||||
}}
|
||||
min="1"
|
||||
max={pages.length}
|
||||
value={pageNumber}
|
||||
disabled={!doc}
|
||||
type="number"
|
||||
class="!w-[45px] !px-1 !py-0"
|
||||
/>
|
||||
<span class="whitespace-nowrap pl-1">
|
||||
/ {pages.length}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex justify-end items-center px-2 text-gray-600 text-sm">
|
||||
<Button
|
||||
on:click={downloadPdf}
|
||||
disabled={!doc}
|
||||
size="xs"
|
||||
color="light"
|
||||
variant="border"
|
||||
title="Download PDF"
|
||||
aria-label="Download PDF"
|
||||
btnClasses="!font-medium !px-2"
|
||||
>
|
||||
{#if wideView}
|
||||
<span class="mr-1"> Download </span>
|
||||
<Button
|
||||
on:click={() => (zoom = 100)}
|
||||
disabled={!doc}
|
||||
size="xs"
|
||||
color="light"
|
||||
variant="border"
|
||||
title="Reset zoom"
|
||||
aria-label="Reset zoom"
|
||||
btnClasses="!w-[50px] !font-medium !rounded-none !border-l-0 !px-1"
|
||||
>
|
||||
{zoom.toFixed(0)}%
|
||||
</Button>
|
||||
{/if}
|
||||
<Download size={16} />
|
||||
</Button>
|
||||
<Button
|
||||
on:click={() => renderPdf(true, true)}
|
||||
disabled={!doc}
|
||||
size="xs"
|
||||
color="light"
|
||||
variant="border"
|
||||
title="Scale to viewport"
|
||||
aria-label="Scale to viewport"
|
||||
btnClasses="!rounded-none !border-l-0 !px-2"
|
||||
>
|
||||
<MoveHorizontal size={16} />
|
||||
</Button>
|
||||
<Button
|
||||
on:click={() => zoom && (zoom += 10)}
|
||||
disabled={!doc}
|
||||
size="xs"
|
||||
color="light"
|
||||
variant="border"
|
||||
title="Zoom in"
|
||||
aria-label="Zoom in"
|
||||
btnClasses="!rounded-l-none !px-2 !border-l-0"
|
||||
>
|
||||
<ZoomIn size={16} />
|
||||
</Button>
|
||||
</div>
|
||||
<div class="center-center px-2 text-gray-600 text-sm">
|
||||
<input
|
||||
on:input={({ currentTarget }) => {
|
||||
scrollToPage(currentTarget.valueAsNumber)
|
||||
}}
|
||||
min="1"
|
||||
max={pages.length}
|
||||
value={pageNumber}
|
||||
disabled={!doc}
|
||||
type="number"
|
||||
class="!w-[45px] !px-1 !py-0"
|
||||
/>
|
||||
<span class="whitespace-nowrap pl-1">
|
||||
/ {pages.length}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex justify-end items-center px-2 text-gray-600 text-sm">
|
||||
<Button
|
||||
on:click={downloadPdf}
|
||||
disabled={!doc}
|
||||
size="xs"
|
||||
color="light"
|
||||
variant="border"
|
||||
title="Download PDF"
|
||||
aria-label="Download PDF"
|
||||
btnClasses="!font-medium !px-2"
|
||||
>
|
||||
{#if wideView}
|
||||
<span class="mr-1"> Download </span>
|
||||
{/if}
|
||||
<Download size={16} />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
{:else}
|
||||
<div
|
||||
out:fade={{ duration: 200 }}
|
||||
class="absolute inset-0 center-center flex-col text-center text-sm bg-white text-gray-600"
|
||||
>
|
||||
<Loader2 class="animate-spin mb-2" />
|
||||
Loading PDF
|
||||
</div>
|
||||
{/if}
|
||||
<div
|
||||
out:fade={{ duration: 200 }}
|
||||
class="absolute inset-0 center-center flex-col text-center text-sm bg-white text-gray-600"
|
||||
bind:this={wrapper}
|
||||
on:scroll={throttledScroll}
|
||||
class={twMerge('w-full h-full overflow-auto', css?.container?.class ?? '', 'bg-gray-100')}
|
||||
style="padding-top: {controlsHeight ?? 0}px; {css?.container?.style ?? ''}"
|
||||
/>
|
||||
{/if}
|
||||
{#if $mode !== 'preview' && $selectedComponent === id}
|
||||
<button
|
||||
class="fixed z-10 bottom-0 left-0 px-2 py-0.5 bg-indigo-500/90
|
||||
hover:bg-indigo-500 focus:bg-indigo-500 duration-200 text-white text-2xs"
|
||||
on:click={() => syncZoomValue()}
|
||||
>
|
||||
<Loader2 class="animate-spin mb-2" />
|
||||
Loading PDF
|
||||
Sync zoom value
|
||||
</button>
|
||||
{/if}
|
||||
{#if error}
|
||||
<div
|
||||
class="absolute inset-0 z-20 center-center
|
||||
bg-gray-100 text-center text-gray-600 text-sm"
|
||||
>
|
||||
{error}
|
||||
</div>
|
||||
{/if}
|
||||
<div
|
||||
bind:this={wrapper}
|
||||
on:scroll={throttledScroll}
|
||||
class={twMerge('w-full h-full overflow-auto', css?.container?.class ?? '', 'bg-gray-100')}
|
||||
style="padding-top: {controlsHeight ?? 0}px; {css?.container?.style ?? ''}"
|
||||
/>
|
||||
{/if}
|
||||
{#if $mode !== 'preview' && $selectedComponent === id}
|
||||
<button
|
||||
class="fixed z-10 bottom-0 left-0 px-2 py-0.5 bg-indigo-500/90
|
||||
hover:bg-indigo-500 focus:bg-indigo-500 duration-200 text-white text-2xs"
|
||||
on:click={() => syncZoomValue()}
|
||||
>
|
||||
Sync zoom value
|
||||
</button>
|
||||
{/if}
|
||||
{#if error}
|
||||
<div
|
||||
class="absolute inset-0 z-20 center-center
|
||||
bg-gray-100 text-center text-gray-600 text-sm"
|
||||
>
|
||||
{error}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import { getContext } from 'svelte'
|
||||
|
||||
@@ -23,9 +23,10 @@
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let initializing: boolean | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
export const staticOutputs: string[] = ['loading', 'result']
|
||||
const { app } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
ChartJS.register(
|
||||
Title,
|
||||
@@ -72,7 +73,7 @@
|
||||
<InputValue {id} input={configuration.theme} bind:value={theme} />
|
||||
<InputValue {id} input={configuration.doughnutStyle} bind:value={doughnut} />
|
||||
|
||||
<RunnableWrapper flexWrap autoRefresh {componentInput} {id} bind:initializing bind:result>
|
||||
<RunnableWrapper {render} flexWrap autoRefresh {componentInput} {id} bind:initializing bind:result>
|
||||
<div class="w-full h-full {css?.container?.class ?? ''}" style={css?.container?.style ?? ''}>
|
||||
{#if result}
|
||||
{#if doughnut}
|
||||
|
||||
@@ -19,19 +19,20 @@
|
||||
import type { ChartOptions, ChartData } from 'chart.js'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
|
||||
export let id: string
|
||||
export let componentInput: AppInput | undefined
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let initializing: boolean | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
let zoomable = false
|
||||
let pannable = false
|
||||
|
||||
export const staticOutputs: string[] = ['loading', 'result']
|
||||
const { app } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
ChartJS.register(
|
||||
Title,
|
||||
@@ -78,7 +79,7 @@
|
||||
<InputValue {id} input={configuration.zoomable} bind:value={zoomable} />
|
||||
<InputValue {id} input={configuration.pannable} bind:value={pannable} />
|
||||
|
||||
<RunnableWrapper flexWrap autoRefresh {componentInput} {id} bind:initializing bind:result>
|
||||
<RunnableWrapper {render} flexWrap autoRefresh {componentInput} {id} bind:initializing bind:result>
|
||||
<div class="w-full h-full {css?.container?.class ?? ''}" style={css?.container?.style ?? ''}>
|
||||
{#if result}
|
||||
<Scatter {data} {options} />
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import { getContext } from 'svelte'
|
||||
import ResizeWrapper from '../helpers/ResizeWrapper.svelte'
|
||||
|
||||
@@ -19,10 +19,11 @@
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let initializing: boolean | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'text'> | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
export const staticOutputs: string[] = ['result', 'loading']
|
||||
|
||||
const { app } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let result: string | undefined = undefined
|
||||
let style: 'Title' | 'Subtitle' | 'Body' | 'Caption' | 'Label' | undefined = undefined
|
||||
@@ -67,7 +68,7 @@
|
||||
<InputValue {id} input={configuration.copyButton} bind:value={copyButton} />
|
||||
<InputValue {id} input={configuration.fitContent} bind:value={fitContent} />
|
||||
|
||||
<RunnableWrapper flexWrap {componentInput} {id} bind:initializing bind:result>
|
||||
<RunnableWrapper {render} flexWrap {componentInput} {id} bind:initializing bind:result>
|
||||
<ResizeWrapper {id} shouldWrap={fitContent}>
|
||||
<AlignWrapper {horizontalAlignment} {verticalAlignment}>
|
||||
{#if !result || result === ''}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
import { Scatter } from 'svelte-chartjs'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import type { ChartOptions, ChartData } from 'chart.js'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import { getContext } from 'svelte'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
|
||||
@@ -29,9 +29,10 @@
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let initializing: boolean | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
export const staticOutputs: string[] = ['loading', 'result']
|
||||
const { app } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let logarithmicScale = false
|
||||
let zoomable = false
|
||||
@@ -93,7 +94,7 @@
|
||||
<InputValue {id} input={configuration.zoomable} bind:value={zoomable} />
|
||||
<InputValue {id} input={configuration.pannable} bind:value={pannable} />
|
||||
|
||||
<RunnableWrapper flexWrap autoRefresh {componentInput} {id} bind:initializing bind:result>
|
||||
<RunnableWrapper {render} flexWrap autoRefresh {componentInput} {id} bind:initializing bind:result>
|
||||
<div class="w-full h-full {css?.container?.class ?? ''}" style={css?.container?.style ?? ''}>
|
||||
{#if result}
|
||||
<Scatter {data} {options} />
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
export let componentInput: AppInput | undefined
|
||||
// export let configuration: Record<string, AppInput>
|
||||
export let initializing: boolean | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
export const staticOutputs: string[] = ['result', 'loading']
|
||||
|
||||
@@ -15,18 +16,17 @@
|
||||
|
||||
let Plotly
|
||||
onMount(async () => {
|
||||
if (divEl) {
|
||||
//@ts-ignore
|
||||
await import('https://cdn.plot.ly/plotly-2.18.0.min.js')
|
||||
//@ts-ignore
|
||||
await import('https://cdn.plot.ly/plotly-2.18.0.min.js')
|
||||
|
||||
Plotly = window['Plotly']
|
||||
}
|
||||
Plotly = window['Plotly']
|
||||
})
|
||||
|
||||
let h: number | undefined = undefined
|
||||
let w: number | undefined = undefined
|
||||
|
||||
$: Plotly &&
|
||||
render &&
|
||||
result &&
|
||||
divEl &&
|
||||
h &&
|
||||
@@ -40,7 +40,7 @@
|
||||
</script>
|
||||
|
||||
<div class="w-full h-full" bind:clientHeight={h} bind:clientWidth={w}>
|
||||
<RunnableWrapper flexWrap {componentInput} {id} bind:initializing bind:result>
|
||||
<RunnableWrapper {render} flexWrap {componentInput} {id} bind:initializing bind:result>
|
||||
<div on:pointerdown bind:this={divEl} />
|
||||
</RunnableWrapper>
|
||||
</div>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
export let componentInput: AppInput | undefined
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let initializing: boolean | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
export const staticOutputs: string[] = ['result', 'loading']
|
||||
|
||||
@@ -52,7 +53,7 @@
|
||||
<InputValue {id} input={configuration.canvas} bind:value={canvas} />
|
||||
|
||||
<div class="w-full h-full" bind:clientHeight={h} bind:clientWidth={w}>
|
||||
<RunnableWrapper flexWrap {componentInput} {id} bind:initializing bind:result>
|
||||
<RunnableWrapper {render} flexWrap {componentInput} {id} bind:initializing bind:result>
|
||||
<div on:pointerdown bind:this={divEl} />
|
||||
</RunnableWrapper>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { getContext, onMount } from 'svelte'
|
||||
import type { Output } from '../../../rx'
|
||||
import type { AppEditorContext } from '../../../types'
|
||||
import type { AppViewerContext } from '../../../types'
|
||||
import InputValue from '../../helpers/InputValue.svelte'
|
||||
import type { AppInput } from '../../../inputType'
|
||||
import RunnableWrapper from '../../helpers/RunnableWrapper.svelte'
|
||||
@@ -17,16 +17,13 @@
|
||||
export let componentInput: AppInput | undefined
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let initializing: boolean | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
export const staticOutputs: string[] = ['selectedRow', 'loading', 'result', 'selectedRowIndex']
|
||||
|
||||
let result: Record<string, any>[] | undefined = undefined
|
||||
|
||||
const {
|
||||
worldStore,
|
||||
staticOutputs: staticOutputsStore,
|
||||
selectedComponent
|
||||
} = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { worldStore, selectedComponent } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let selectedRowIndex = -1
|
||||
|
||||
@@ -84,7 +81,7 @@
|
||||
<InputValue {id} input={configuration.pagination} bind:value={pagination} />
|
||||
<InputValue {id} input={configuration.pageSize} bind:value={pageSize} />
|
||||
|
||||
<RunnableWrapper flexWrap {componentInput} {id} bind:initializing bind:result>
|
||||
<RunnableWrapper {render} flexWrap {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"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { getContext, onMount } from 'svelte'
|
||||
import type { Output } from '../../../rx'
|
||||
import type { AppEditorContext, BaseAppComponent, ComponentCustomCSS } from '../../../types'
|
||||
import type { AppViewerContext, BaseAppComponent, ComponentCustomCSS } from '../../../types'
|
||||
import InputValue from '../../helpers/InputValue.svelte'
|
||||
import type { AppInput } from '../../../inputType'
|
||||
import RunnableWrapper from '../../helpers/RunnableWrapper.svelte'
|
||||
@@ -25,6 +25,7 @@
|
||||
export let customCss:
|
||||
| ComponentCustomCSS<'container' | 'tableHeader' | 'tableBody' | 'tableFooter'>
|
||||
| undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
export const staticOutputs: string[] = [
|
||||
'selectedRow',
|
||||
@@ -60,7 +61,7 @@
|
||||
app,
|
||||
worldStore,
|
||||
staticOutputs: staticOutputsStore
|
||||
} = getContext<AppEditorContext>('AppEditorContext')
|
||||
} = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let selectedRowIndex = -1
|
||||
|
||||
@@ -150,7 +151,7 @@
|
||||
|
||||
<InputValue {id} input={configuration.search} bind:value={search} />
|
||||
|
||||
<RunnableWrapper flexWrap {componentInput} {id} bind:initializing bind:result>
|
||||
<RunnableWrapper {render} flexWrap {componentInput} {id} bind:initializing bind:result>
|
||||
{#if Array.isArray(result) && result.every(isObject)}
|
||||
<div
|
||||
class={twMerge(
|
||||
@@ -247,6 +248,7 @@
|
||||
{#each actionButtons as actionButton, actionIndex (actionIndex)}
|
||||
{#if rowIndex == 0}
|
||||
<AppButton
|
||||
{render}
|
||||
noWFull
|
||||
{...actionButton}
|
||||
preclickAction={async () => {
|
||||
@@ -258,6 +260,7 @@
|
||||
/>
|
||||
{:else}
|
||||
<AppButton
|
||||
{render}
|
||||
noWFull
|
||||
{...actionButton}
|
||||
preclickAction={async () => {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
let c = ''
|
||||
export { c as class }
|
||||
export let style = ''
|
||||
export let render: boolean = true
|
||||
|
||||
function tailwindHorizontalAlignment(alignment?: HorizontalAlignment) {
|
||||
if (!alignment) return ''
|
||||
@@ -39,6 +40,8 @@
|
||||
)
|
||||
</script>
|
||||
|
||||
<div class={classes} {style}>
|
||||
<slot />
|
||||
</div>
|
||||
{#if render}
|
||||
<div class={classes} {style}>
|
||||
<slot />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
</script>
|
||||
|
||||
<RunnableComponent
|
||||
render={false}
|
||||
{id}
|
||||
{fields}
|
||||
bind:result
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppInput, EvalAppInput, UploadAppInput } from '../../inputType'
|
||||
import type { AppEditorContext } from '../../types'
|
||||
import type { AppViewerContext } from '../../types'
|
||||
import { accessPropertyByPath } from '../../utils'
|
||||
|
||||
type T = string | number | boolean | Record<string | number, any> | undefined
|
||||
@@ -23,7 +23,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
const { worldStore } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { worldStore } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
$: state = $worldStore?.state
|
||||
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
<script lang="ts">
|
||||
import { Loader2 } from 'lucide-svelte'
|
||||
import { getContext } from 'svelte'
|
||||
import { fade } from 'svelte/transition'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppEditorContext } from '../../types'
|
||||
import type { AppViewerContext } from '../../types'
|
||||
import InputValue from './InputValue.svelte'
|
||||
|
||||
export let componentInput: AppInput
|
||||
export let id: string
|
||||
export let result: any
|
||||
export let render: boolean
|
||||
|
||||
// Sync the result to the output
|
||||
const { worldStore } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { worldStore } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
$: outputs = $worldStore?.outputsById[id] as {
|
||||
loading: Output<boolean>
|
||||
@@ -32,4 +35,16 @@
|
||||
<InputValue {id} input={componentInput} bind:value={result} />
|
||||
{/if}
|
||||
|
||||
<slot />
|
||||
{#if render}
|
||||
<slot />
|
||||
{:else}
|
||||
<div class="w-full h-full">
|
||||
<div
|
||||
out:fade|local={{ duration: 50 }}
|
||||
class="absolute inset-0 center-center flex-col bg-white text-gray-600 border"
|
||||
>
|
||||
<Loader2 class="animate-spin" size={16} />
|
||||
<span class="text-xs mt-1">Loading</span>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
import { faRefresh } from '@fortawesome/free-solid-svg-icons'
|
||||
import { RefreshCcw, RefreshCw } from 'lucide-svelte'
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppEditorContext } from '../../types'
|
||||
import type { AppViewerContext } from '../../types'
|
||||
|
||||
export let componentId: string
|
||||
|
||||
const { runnableComponents, worldStore } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { runnableComponents, worldStore } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
async function refresh() {
|
||||
window.dispatchEvent(new Event('pointerup'))
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte'
|
||||
import { findGridItem } from '../../editor/appUtils'
|
||||
import type { AppEditorContext } from '../../types'
|
||||
import type { AppViewerContext } from '../../types'
|
||||
|
||||
export let id: string
|
||||
export let shouldWrap: boolean = false
|
||||
const { app, breakpoint } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { app, breakpoint } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
$: gridItem = findGridItem($app, id)
|
||||
|
||||
|
||||
@@ -7,11 +7,12 @@
|
||||
import TestJobLoader from '$lib/components/TestJobLoader.svelte'
|
||||
import { AppService, type CompletedJob } from '$lib/gen'
|
||||
import { classNames, defaultIfEmptyString, emptySchema, sendUserToast } from '$lib/utils'
|
||||
import { Bug } from 'lucide-svelte'
|
||||
import { Bug, Loader2 } from 'lucide-svelte'
|
||||
import { getContext, onMount } from 'svelte'
|
||||
import { fade } from 'svelte/transition'
|
||||
import type { AppInputs, Runnable } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppEditorContext } from '../../types'
|
||||
import type { AppViewerContext } from '../../types'
|
||||
import InputValue from './InputValue.svelte'
|
||||
import RefreshButton from './RefreshButton.svelte'
|
||||
|
||||
@@ -29,6 +30,7 @@
|
||||
export let initializing: boolean | undefined = undefined
|
||||
export let gotoUrl: string | undefined = undefined
|
||||
export let gotoNewTab: boolean | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
const {
|
||||
worldStore,
|
||||
@@ -41,7 +43,7 @@
|
||||
errorByComponent,
|
||||
mode,
|
||||
stateId
|
||||
} = getContext<AppEditorContext>('AppEditorContext')
|
||||
} = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
onMount(() => {
|
||||
if (autoRefresh) {
|
||||
@@ -58,7 +60,6 @@
|
||||
let executeTimeout: NodeJS.Timeout | undefined = undefined
|
||||
|
||||
function setDebouncedExecute() {
|
||||
console.log('EXECUTE')
|
||||
executeTimeout && clearTimeout(executeTimeout)
|
||||
executeTimeout = setTimeout(() => {
|
||||
executeComponent(true)
|
||||
@@ -86,7 +87,6 @@
|
||||
}
|
||||
|
||||
$: fields && (lazyStaticValues = computeStaticValues())
|
||||
$: console.log(runnableInputValues, extraQueryParams, args, autoRefresh, testJobLoader)
|
||||
$: (runnableInputValues || extraQueryParams || args) &&
|
||||
autoRefresh &&
|
||||
testJobLoader &&
|
||||
@@ -258,57 +258,61 @@
|
||||
bind:this={testJobLoader}
|
||||
/>
|
||||
|
||||
<div class="h-full flex relative flex-row flex-wrap {wrapperClass}" style={wrapperStyle}>
|
||||
{#if schemaStripped && Object.keys(schemaStripped?.properties ?? {}).length > 0 && (autoRefresh || forceSchemaDisplay)}
|
||||
<div class="px-2 h-fit min-h-0">
|
||||
<SchemaForm
|
||||
{flexWrap}
|
||||
schema={schemaStripped}
|
||||
bind:args
|
||||
{disabledArgs}
|
||||
shouldHideNoInputs
|
||||
noVariablePicker
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{#if render}
|
||||
<div class="h-full flex relative flex-row flex-wrap {wrapperClass}" style={wrapperStyle}>
|
||||
{#if schemaStripped && Object.keys(schemaStripped?.properties ?? {}).length > 0 && (autoRefresh || forceSchemaDisplay)}
|
||||
<div class="px-2 h-fit min-h-0">
|
||||
<SchemaForm
|
||||
{flexWrap}
|
||||
schema={schemaStripped}
|
||||
bind:args
|
||||
{disabledArgs}
|
||||
shouldHideNoInputs
|
||||
noVariablePicker
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if !runnable && autoRefresh}
|
||||
<Alert type="warning" size="xs" class="mt-2 px-1" title="Missing runnable">
|
||||
Please select a runnable
|
||||
</Alert>
|
||||
{:else if result?.error && $mode === 'preview'}
|
||||
<div
|
||||
title="Error"
|
||||
class={classNames(
|
||||
'text-red-500 px-1 text-2xs py-0.5 font-bold w-fit absolute border border-red-500 -bottom-2 shadow left-1/2 transform -translate-x-1/2 z-50 cursor-pointer',
|
||||
'bg-red-100/80'
|
||||
)}
|
||||
>
|
||||
<Popover notClickable placement="bottom" popupClass="!bg-white border w-96">
|
||||
<Bug size={14} />
|
||||
<span slot="text">
|
||||
<div class="bg-white">
|
||||
<Alert type="error" title="Error during execution">
|
||||
<div class="flex flex-col gap-2">
|
||||
An error occured, please contact the app author.
|
||||
<span class="font-semibold">Job id: {testJob?.id}</span>
|
||||
</div>
|
||||
</Alert>
|
||||
</div>
|
||||
</span>
|
||||
</Popover>
|
||||
</div>
|
||||
<div class="block grow w-full max-h-full border border-red-300 bg-red-50 relative">
|
||||
<slot />
|
||||
</div>
|
||||
{:else}
|
||||
<div class="block grow w-full max-h-full">
|
||||
<slot />
|
||||
</div>
|
||||
{/if}
|
||||
{#if !initializing && autoRefresh === true}
|
||||
<div class="flex absolute top-1 right-1 z-50">
|
||||
<RefreshButton componentId={id} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{#if !runnable && autoRefresh}
|
||||
<Alert type="warning" size="xs" class="mt-2 px-1" title="Missing runnable">
|
||||
Please select a runnable
|
||||
</Alert>
|
||||
{:else if result?.error && $mode === 'preview'}
|
||||
<div
|
||||
title="Error"
|
||||
class={classNames(
|
||||
'text-red-500 px-1 text-2xs py-0.5 font-bold w-fit absolute border border-red-500 -bottom-2 shadow left-1/2 transform -translate-x-1/2 z-50 cursor-pointer',
|
||||
'bg-red-100/80'
|
||||
)}
|
||||
>
|
||||
<Popover notClickable placement="bottom" popupClass="!bg-white border w-96">
|
||||
<Bug size={14} />
|
||||
<span slot="text">
|
||||
<div class="bg-white">
|
||||
<Alert type="error" title="Error during execution">
|
||||
<div class="flex flex-col gap-2">
|
||||
An error occured, please contact the app author.
|
||||
<span class="font-semibold">Job id: {testJob?.id}</span>
|
||||
</div>
|
||||
</Alert>
|
||||
</div>
|
||||
</span>
|
||||
</Popover>
|
||||
</div>
|
||||
<div class="block grow w-full max-h-full border border-red-300 bg-red-50 relative">
|
||||
<slot />
|
||||
</div>
|
||||
{:else}
|
||||
<div class="block grow w-full max-h-full">
|
||||
<slot />
|
||||
</div>
|
||||
{/if}
|
||||
{#if !initializing && autoRefresh === true}
|
||||
<div class="flex absolute top-1 right-1 z-50">
|
||||
<RefreshButton componentId={id} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="w-full h-full" />
|
||||
{/if}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { getContext, onMount } from 'svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { AppEditorContext } from '../../types'
|
||||
import type { AppViewerContext } from '../../types'
|
||||
import { isScriptByNameDefined, isScriptByPathDefined } from '../../utils'
|
||||
import NonRunnableComponent from './NonRunnableComponent.svelte'
|
||||
import RunnableComponent from './RunnableComponent.svelte'
|
||||
@@ -20,8 +20,9 @@
|
||||
export let runnableStyle = ''
|
||||
export let goto: string | undefined = undefined
|
||||
export let gotoNewTab: boolean | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
const { staticExporter, noBackend } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { staticExporter, noBackend } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
$: if (initializing && result) {
|
||||
initializing = false
|
||||
@@ -57,11 +58,12 @@
|
||||
{initializing}
|
||||
wrapperClass={runnableClass}
|
||||
wrapperStyle={runnableStyle}
|
||||
{render}
|
||||
>
|
||||
<slot />
|
||||
</RunnableComponent>
|
||||
{:else}
|
||||
<NonRunnableComponent bind:result {id} {componentInput}>
|
||||
<NonRunnableComponent {render} bind:result {id} {componentInput}>
|
||||
<slot />
|
||||
</NonRunnableComponent>
|
||||
{/if}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
@@ -13,8 +13,9 @@
|
||||
export let horizontalAlignment: 'left' | 'center' | 'right' | undefined = undefined
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'text'> | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
const { app, worldStore } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { app, worldStore } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
export const staticOutputs: string[] = ['result']
|
||||
|
||||
@@ -36,7 +37,7 @@
|
||||
<InputValue {id} input={configuration.label} bind:value={labelValue} />
|
||||
<InputValue {id} input={configuration.defaultValue} bind:value={defaultValue} />
|
||||
|
||||
<AlignWrapper {horizontalAlignment} {verticalAlignment}>
|
||||
<AlignWrapper {render} {horizontalAlignment} {verticalAlignment}>
|
||||
<Toggle
|
||||
on:pointerdown={(e) => {
|
||||
e?.stopPropagation()
|
||||
|
||||
@@ -3,36 +3,38 @@
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputDefaultValue from '../helpers/InputDefaultValue.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
|
||||
export let id: string
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let inputType: 'date' | 'time' | 'datetime-local'
|
||||
export let inputType: 'date'
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
export const staticOutputs: string[] = ['result']
|
||||
export let customCss: ComponentCustomCSS<'input'> | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
const { app, worldStore } = getContext<AppEditorContext>('AppEditorContext')
|
||||
let input: HTMLInputElement
|
||||
const { app, worldStore, selectedComponent } = getContext<AppViewerContext>('AppViewerContext')
|
||||
let labelValue: string = 'Title'
|
||||
let minValue: string = ''
|
||||
let maxValue: string = ''
|
||||
let defaultValue: string | undefined = undefined
|
||||
|
||||
let value: string | undefined = undefined
|
||||
|
||||
$: outputs = $worldStore?.outputsById[id] as {
|
||||
result: Output<string>
|
||||
result: Output<string | undefined>
|
||||
}
|
||||
|
||||
function handleInput() {
|
||||
outputs?.result.set(input.value)
|
||||
$: handleDefault(defaultValue)
|
||||
|
||||
$: outputs?.result.set(value)
|
||||
|
||||
function handleDefault(defaultValue: string | undefined) {
|
||||
value = defaultValue
|
||||
}
|
||||
|
||||
$: input && handleInput()
|
||||
|
||||
$: css = concatCustomCss($app.css?.dateinputcomponent, customCss)
|
||||
</script>
|
||||
|
||||
@@ -41,17 +43,18 @@
|
||||
<InputValue {id} input={configuration.maxDate} bind:value={maxValue} />
|
||||
<InputValue {id} input={configuration.defaultValue} bind:value={defaultValue} />
|
||||
|
||||
<InputDefaultValue bind:input {defaultValue} />
|
||||
|
||||
<AlignWrapper {verticalAlignment}>
|
||||
<input
|
||||
type={inputType}
|
||||
bind:this={input}
|
||||
on:input={handleInput}
|
||||
min={minValue}
|
||||
max={maxValue}
|
||||
placeholder="Type..."
|
||||
class={twMerge('mx-0.5', css?.input?.class ?? '')}
|
||||
style={css?.input?.style ?? ''}
|
||||
/>
|
||||
<AlignWrapper {render} {verticalAlignment}>
|
||||
{#if inputType === 'date'}
|
||||
<input
|
||||
on:focus={() => ($selectedComponent = id)}
|
||||
on:pointerdown|stopPropagation
|
||||
type="date"
|
||||
bind:value
|
||||
min={minValue}
|
||||
max={maxValue}
|
||||
placeholder="Type..."
|
||||
class={twMerge('mx-0.5', css?.input?.class ?? '')}
|
||||
style={css?.input?.style ?? ''}
|
||||
/>
|
||||
{/if}
|
||||
</AlignWrapper>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import { FileInput } from '../../../common'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
|
||||
@@ -12,8 +12,9 @@
|
||||
export let configuration: Record<string, AppInput>
|
||||
export const staticOutputs: string[] = ['result']
|
||||
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
const { app, worldStore } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { app, worldStore } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let acceptedFileTypes: string[] | undefined = undefined
|
||||
let allowMultiple: boolean | undefined = undefined
|
||||
@@ -35,17 +36,19 @@
|
||||
<InputValue {id} input={configuration.allowMultiple} bind:value={allowMultiple} />
|
||||
<InputValue {id} input={configuration.text} bind:value={text} />
|
||||
|
||||
<div class="w-full h-full p-1">
|
||||
<FileInput
|
||||
accept={acceptedFileTypes?.length ? acceptedFileTypes?.join(', ') : undefined}
|
||||
multiple={allowMultiple}
|
||||
convertTo="base64"
|
||||
on:change={({ detail }) => {
|
||||
handleChange(detail)
|
||||
}}
|
||||
class={twMerge('w-full h-full', css?.container?.class)}
|
||||
style={css?.container?.style}
|
||||
>
|
||||
{text}
|
||||
</FileInput>
|
||||
</div>
|
||||
{#if render}
|
||||
<div class="w-full h-full p-1">
|
||||
<FileInput
|
||||
accept={acceptedFileTypes?.length ? acceptedFileTypes?.join(', ') : undefined}
|
||||
multiple={allowMultiple}
|
||||
convertTo="base64"
|
||||
on:change={({ detail }) => {
|
||||
handleChange(detail)
|
||||
}}
|
||||
class={twMerge('w-full h-full', css?.container?.class)}
|
||||
style={css?.container?.style}
|
||||
>
|
||||
{text}
|
||||
</FileInput>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -3,12 +3,11 @@
|
||||
import Select from 'svelte-select'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import { SELECT_INPUT_DEFAULT_STYLE } from '../../../../defaults'
|
||||
import { stopPropagation } from 'ol/events/Event'
|
||||
|
||||
export const staticOutputs: string[] = ['result']
|
||||
export let id: string
|
||||
@@ -16,9 +15,10 @@
|
||||
export let horizontalAlignment: 'left' | 'center' | 'right' | undefined = undefined
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'input'> | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
const { app, worldStore, connectingInput, selectedComponent } =
|
||||
getContext<AppEditorContext>('AppEditorContext')
|
||||
getContext<AppViewerContext>('AppViewerContext')
|
||||
let items: { label: string; value: string }[]
|
||||
let placeholder: string = 'Select an item'
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
<InputValue {id} input={configuration.items} bind:value={labels} />
|
||||
<InputValue {id} input={configuration.placeholder} bind:value={placeholder} />
|
||||
|
||||
<AlignWrapper {horizontalAlignment} {verticalAlignment}>
|
||||
<AlignWrapper {render} {horizontalAlignment} {verticalAlignment}>
|
||||
<div class="app-select w-full mx-0.5" style="height: 34px" on:pointerdown|stopPropagation>
|
||||
{#if !value || Array.isArray(value)}
|
||||
<Select
|
||||
|
||||
@@ -3,10 +3,9 @@
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputDefaultValue from '../helpers/InputDefaultValue.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
|
||||
export let id: string
|
||||
@@ -14,28 +13,29 @@
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
export const staticOutputs: string[] = ['result']
|
||||
export let customCss: ComponentCustomCSS<'input'> | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
const { app, worldStore } = getContext<AppEditorContext>('AppEditorContext')
|
||||
let input: HTMLInputElement
|
||||
const { app, worldStore, selectedComponent } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let defaultValue: number | undefined = undefined
|
||||
let placeholder: string | undefined = undefined
|
||||
let value: number | undefined = undefined
|
||||
|
||||
let min: number | undefined = undefined
|
||||
let max: number | undefined = undefined
|
||||
let step = 1
|
||||
|
||||
$: outputs = $worldStore?.outputsById[id] as {
|
||||
result: Output<number | null>
|
||||
result: Output<number | undefined>
|
||||
}
|
||||
|
||||
function handleInput() {
|
||||
const value = input?.value
|
||||
const num = isNaN(+value) ? null : +value
|
||||
outputs?.result.set(num)
|
||||
}
|
||||
$: handleDefault(defaultValue)
|
||||
|
||||
$: input && handleInput()
|
||||
$: outputs?.result.set(value)
|
||||
|
||||
function handleDefault(defaultValue: number | undefined) {
|
||||
value = defaultValue
|
||||
}
|
||||
|
||||
$: css = concatCustomCss($app.css?.numberinputcomponent, customCss)
|
||||
</script>
|
||||
@@ -45,21 +45,17 @@
|
||||
<InputValue {id} input={configuration.max} bind:value={max} />
|
||||
<InputValue {id} input={configuration.placeholder} bind:value={placeholder} />
|
||||
<InputValue {id} input={configuration.defaultValue} bind:value={defaultValue} />
|
||||
<InputDefaultValue bind:input {defaultValue} />
|
||||
|
||||
<AlignWrapper {verticalAlignment}>
|
||||
<AlignWrapper {render} {verticalAlignment}>
|
||||
<input
|
||||
on:pointerdown|stopPropagation
|
||||
on:focus={() => ($selectedComponent = id)}
|
||||
class={twMerge(
|
||||
'windmillapp w-full py-1.5 text-sm focus:ring-indigo-100 px-2 mx-0.5',
|
||||
css?.input?.class ?? ''
|
||||
)}
|
||||
style={css?.input?.style ?? ''}
|
||||
bind:this={input}
|
||||
on:input={handleInput}
|
||||
on:focus={(e) => {
|
||||
e?.stopPropagation()
|
||||
window.dispatchEvent(new Event('pointerup'))
|
||||
}}
|
||||
bind:value
|
||||
{min}
|
||||
{max}
|
||||
{step}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
@@ -15,8 +15,9 @@
|
||||
export const staticOutputs: string[] = ['result']
|
||||
export let customCss: ComponentCustomCSS<'handles' | 'bar' | 'limits' | 'values'> | undefined =
|
||||
undefined
|
||||
export let render: boolean
|
||||
|
||||
const { app, worldStore } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { app, worldStore } = getContext<AppViewerContext>('AppViewerContext')
|
||||
let min = 0
|
||||
let max = 42
|
||||
let step = 1
|
||||
@@ -48,7 +49,7 @@
|
||||
<InputValue {id} input={configuration.defaultLow} bind:value={values[0]} />
|
||||
<InputValue {id} input={configuration.defaultHigh} bind:value={values[1]} />
|
||||
|
||||
<AlignWrapper {verticalAlignment}>
|
||||
<AlignWrapper {render} {verticalAlignment}>
|
||||
<div class="flex flex-col w-full">
|
||||
<div class="flex items-center w-full gap-1 px-1">
|
||||
<span class={css?.limits?.class ?? ''} style={css?.limits?.style ?? ''}>
|
||||
|
||||
@@ -3,13 +3,11 @@
|
||||
import Select from 'svelte-select'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import { SELECT_INPUT_DEFAULT_STYLE } from '../../../../defaults'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import Portal from 'svelte-portal'
|
||||
|
||||
export const staticOutputs: string[] = ['result']
|
||||
export let id: string
|
||||
@@ -17,9 +15,10 @@
|
||||
export let horizontalAlignment: 'left' | 'center' | 'right' | undefined = undefined
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'input'> | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
const { app, worldStore, connectingInput, selectedComponent } =
|
||||
getContext<AppEditorContext>('AppEditorContext')
|
||||
getContext<AppViewerContext>('AppViewerContext')
|
||||
let items: { label: string; value: any; created?: boolean }[]
|
||||
let placeholder: string = 'Select an item'
|
||||
|
||||
@@ -98,7 +97,7 @@
|
||||
<InputValue {id} input={configuration.defaultValue} bind:value={defaultValue} />
|
||||
<InputValue {id} input={configuration.create} bind:value={create} />
|
||||
|
||||
<AlignWrapper {horizontalAlignment} {verticalAlignment}>
|
||||
<AlignWrapper {render} {horizontalAlignment} {verticalAlignment}>
|
||||
<div class="app-select w-full mx-0.5" style="height: 34px;" on:pointerdown|stopPropagation>
|
||||
<Select
|
||||
--border-radius="0"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
@@ -15,8 +15,9 @@
|
||||
export const staticOutputs: string[] = ['result']
|
||||
export let customCss: ComponentCustomCSS<'handle' | 'limits' | 'value' | 'bar'> | undefined =
|
||||
undefined
|
||||
export let render: boolean
|
||||
|
||||
const { app, worldStore, selectedComponent } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { app, worldStore, selectedComponent } = getContext<AppViewerContext>('AppViewerContext')
|
||||
let min = 0
|
||||
let max = 42
|
||||
let step = 1
|
||||
@@ -56,7 +57,7 @@
|
||||
<InputValue {id} input={configuration.max} bind:value={max} />
|
||||
<InputValue {id} input={configuration.defaultValue} bind:value={values[0]} />
|
||||
|
||||
<AlignWrapper {verticalAlignment}>
|
||||
<AlignWrapper {render} {verticalAlignment}>
|
||||
<div class="flex items-center w-full gap-1 px-1">
|
||||
<span class={css?.limits?.class ?? ''} style={css?.limits?.style ?? ''}>
|
||||
{+min}
|
||||
|
||||
@@ -3,10 +3,9 @@
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputDefaultValue from '../helpers/InputDefaultValue.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
|
||||
export let id: string
|
||||
@@ -15,45 +14,73 @@
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
export const staticOutputs: string[] = ['result']
|
||||
export let customCss: ComponentCustomCSS<'input'> | undefined = undefined
|
||||
export let appCssKey: 'textinputcomponent' | 'passwordinputcomponent' = 'textinputcomponent'
|
||||
export let appCssKey: 'textinputcomponent' | 'passwordinputcomponent' | 'emailinputcomponent' =
|
||||
'textinputcomponent'
|
||||
export let render: boolean
|
||||
|
||||
const { app, worldStore } = getContext<AppEditorContext>('AppEditorContext')
|
||||
let input: HTMLInputElement
|
||||
const { app, worldStore, selectedComponent } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let placeholder: string | undefined = undefined
|
||||
let defaultValue: string | undefined = undefined
|
||||
let value: string | undefined = undefined
|
||||
|
||||
$: outputs = $worldStore?.outputsById[id] as {
|
||||
result: Output<string>
|
||||
result: Output<string | undefined>
|
||||
}
|
||||
|
||||
function handleInput() {
|
||||
outputs?.result.set(input.value)
|
||||
}
|
||||
$: handleDefault(defaultValue)
|
||||
|
||||
$: input && handleInput()
|
||||
$: outputs?.result.set(value)
|
||||
|
||||
function handleDefault(defaultValue: string | undefined) {
|
||||
value = defaultValue
|
||||
}
|
||||
|
||||
$: css = concatCustomCss($app.css?.[appCssKey], customCss)
|
||||
</script>
|
||||
|
||||
<InputValue {id} input={configuration.placeholder} bind:value={placeholder} />
|
||||
<InputValue {id} input={configuration.defaultValue} bind:value={defaultValue} />
|
||||
<InputDefaultValue bind:input {defaultValue} />
|
||||
|
||||
<AlignWrapper {verticalAlignment}>
|
||||
<input
|
||||
class={twMerge(
|
||||
'windmillapp w-full py-1.5 text-sm focus:ring-indigo-100 px-2 mx-0.5',
|
||||
css?.input?.class ?? ''
|
||||
)}
|
||||
style={css?.input?.style ?? ''}
|
||||
on:focus={(e) => {
|
||||
e?.stopPropagation()
|
||||
window.dispatchEvent(new Event('pointerup'))
|
||||
}}
|
||||
type={inputType}
|
||||
bind:this={input}
|
||||
on:input={handleInput}
|
||||
{placeholder}
|
||||
/>
|
||||
<AlignWrapper {render} {verticalAlignment}>
|
||||
{#if inputType === 'password'}
|
||||
<input
|
||||
class={twMerge(
|
||||
'windmillapp w-full py-1.5 text-sm focus:ring-indigo-100 px-2 mx-0.5',
|
||||
css?.input?.class ?? ''
|
||||
)}
|
||||
style={css?.input?.style ?? ''}
|
||||
on:pointerdown|stopPropagation
|
||||
on:focus={() => ($selectedComponent = id)}
|
||||
type="password"
|
||||
bind:value
|
||||
{placeholder}
|
||||
/>
|
||||
{:else if inputType === 'text'}
|
||||
<input
|
||||
class={twMerge(
|
||||
'windmillapp w-full py-1.5 text-sm focus:ring-indigo-100 px-2 mx-0.5',
|
||||
css?.input?.class ?? ''
|
||||
)}
|
||||
style={css?.input?.style ?? ''}
|
||||
on:pointerdown|stopPropagation
|
||||
on:focus={() => ($selectedComponent = id)}
|
||||
type="text"
|
||||
bind:value
|
||||
{placeholder}
|
||||
/>
|
||||
{:else if inputType === 'email'}
|
||||
<input
|
||||
class={twMerge(
|
||||
'windmillapp w-full py-1.5 text-sm focus:ring-indigo-100 px-2 mx-0.5',
|
||||
css?.input?.class ?? ''
|
||||
)}
|
||||
style={css?.input?.style ?? ''}
|
||||
on:pointerdown|stopPropagation
|
||||
on:focus={() => ($selectedComponent = id)}
|
||||
type="email"
|
||||
bind:value
|
||||
{placeholder}
|
||||
/>
|
||||
{/if}
|
||||
</AlignWrapper>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import type { AppInput } from '../../../inputType'
|
||||
import type { Output } from '../../../rx'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../../types'
|
||||
import { concatCustomCss } from '../../../utils'
|
||||
import AlignWrapper from '../../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../../helpers/InputValue.svelte'
|
||||
@@ -14,8 +14,9 @@
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
export const staticOutputs: string[] = ['result']
|
||||
export let customCss: ComponentCustomCSS<'input'> | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
const { app, worldStore } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { app, worldStore } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let defaultValue: number | undefined = undefined
|
||||
|
||||
@@ -49,7 +50,7 @@
|
||||
<InputValue {id} input={configuration.currency} bind:value={currency} />
|
||||
<InputValue {id} input={configuration.locale} bind:value={locale} />
|
||||
|
||||
<AlignWrapper {verticalAlignment}>
|
||||
<AlignWrapper {render} {verticalAlignment}>
|
||||
{#key isNegativeAllowed}
|
||||
{#key locale}
|
||||
{#key currency}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { getContext } from 'svelte'
|
||||
import SubGridEditor from '../../editor/SubGridEditor.svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { AppEditorContext, ComponentCustomCSS, GridItem } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS, GridItem } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
|
||||
@@ -10,11 +10,12 @@
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let componentContainerHeight: number
|
||||
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
let noPadding: boolean | undefined = undefined
|
||||
|
||||
export const staticOutputs: string[] = []
|
||||
const { app, focusedGrid, selectedComponent } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { app, focusedGrid, selectedComponent } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
function onFocus() {
|
||||
$focusedGrid = {
|
||||
@@ -30,9 +31,10 @@
|
||||
|
||||
<InputValue {id} input={configuration.noPadding} bind:value={noPadding} />
|
||||
|
||||
<div class="border">
|
||||
<div>
|
||||
{#if $app.subgrids?.[`${id}-0`]}
|
||||
<SubGridEditor
|
||||
visible={render}
|
||||
{noPadding}
|
||||
{id}
|
||||
class={css?.container.class}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type {
|
||||
AppEditorContext,
|
||||
AppViewerContext,
|
||||
ComponentCustomCSS,
|
||||
HorizontalAlignment,
|
||||
VerticalAlignment
|
||||
@@ -18,8 +18,9 @@
|
||||
export let verticalAlignment: VerticalAlignment | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'container' | 'divider'> | undefined = undefined
|
||||
export let position: 'horizontal' | 'vertical'
|
||||
export let render: boolean
|
||||
|
||||
const { app } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
let size = 2
|
||||
let color = '#00000060'
|
||||
|
||||
@@ -34,6 +35,7 @@
|
||||
{verticalAlignment}
|
||||
class={twMerge(css?.container?.class, 'h-full')}
|
||||
style={css?.container?.style}
|
||||
{render}
|
||||
>
|
||||
<div
|
||||
class={twMerge(
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { getContext } from 'svelte'
|
||||
import SubGridEditor from '../../editor/SubGridEditor.svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import Portal from 'svelte-portal'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
@@ -16,8 +16,9 @@
|
||||
export let horizontalAlignment: 'left' | 'center' | 'right' | undefined = undefined
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
export let noWFull = false
|
||||
export let render: boolean
|
||||
|
||||
const { app, focusedGrid, selectedComponent } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { app, focusedGrid, selectedComponent } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let gridContent: string[] | undefined = undefined
|
||||
let noPadding: boolean | undefined = undefined
|
||||
@@ -69,7 +70,7 @@
|
||||
<InputValue {id} input={configuration.fillContainer} bind:value={fillContainer} />
|
||||
|
||||
<Portal target="#app-editor-top-level-drawer">
|
||||
<Drawer bind:this={appDrawer} size="800px" alwaysOpen positionClass="!absolute">
|
||||
<Drawer let:open bind:this={appDrawer} size="800px" alwaysOpen positionClass="!absolute">
|
||||
<DrawerContent
|
||||
title={drawerTitle}
|
||||
on:close={() => {
|
||||
@@ -79,6 +80,7 @@
|
||||
>
|
||||
{#if $app.subgrids?.[`${id}-0`]}
|
||||
<SubGridEditor
|
||||
visible={open && render}
|
||||
{noPadding}
|
||||
{id}
|
||||
class={css?.container.class}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { getContext } from 'svelte'
|
||||
import SubGridEditor from '../../editor/SubGridEditor.svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppEditorContext, AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import { Pane, Splitpanes } from 'svelte-splitpanes'
|
||||
@@ -14,10 +14,11 @@
|
||||
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
|
||||
export let horizontal: boolean = false
|
||||
export let panes: number[]
|
||||
export let render: boolean
|
||||
|
||||
export const staticOutputs: string[] = []
|
||||
const { app, focusedGrid, selectedComponent } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
const { app, focusedGrid, selectedComponent } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { componentControl } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
let noPadding: boolean | undefined = undefined
|
||||
|
||||
@@ -31,6 +32,31 @@
|
||||
$: $selectedComponent === id && onFocus()
|
||||
$: css = concatCustomCss($app.css?.containercomponent, customCss)
|
||||
|
||||
$componentControl[id] = {
|
||||
left: () => {
|
||||
if ($focusedGrid?.subGridIndex) {
|
||||
const index = $focusedGrid?.subGridIndex ?? 0
|
||||
if (index > 0) {
|
||||
$focusedGrid.subGridIndex = index - 1
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
},
|
||||
right: () => {
|
||||
// subGridIndex can be 0
|
||||
if ($focusedGrid?.subGridIndex !== undefined) {
|
||||
const index = $focusedGrid?.subGridIndex ?? 0
|
||||
|
||||
if (index < panes.length - 1) {
|
||||
$focusedGrid.subGridIndex = index + 1
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
let sumedup = [50, 50]
|
||||
$: {
|
||||
let ns = panes.map((x) => (x / panes.reduce((a, b) => a + b, 0)) * 100)
|
||||
@@ -48,6 +74,7 @@
|
||||
<Pane size={paneSize} minSize={20}>
|
||||
{#if $app.subgrids?.[`${id}-${index}`]}
|
||||
<SubGridEditor
|
||||
visible={render}
|
||||
noYPadding
|
||||
{noPadding}
|
||||
{id}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { Tab, Tabs } from '$lib/components/common'
|
||||
import { getContext } from 'svelte'
|
||||
import { getContext, onMount } from 'svelte'
|
||||
import SubGridEditor from '../../editor/SubGridEditor.svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppEditorContext, AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
|
||||
@@ -15,31 +15,26 @@
|
||||
export let customCss:
|
||||
| ComponentCustomCSS<'tabRow' | 'tabs' | 'selectedTab' | 'container'>
|
||||
| undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
export const staticOutputs: string[] = ['selectedTabIndex']
|
||||
const { app, worldStore, focusedGrid, selectedComponent } =
|
||||
getContext<AppEditorContext>('AppEditorContext')
|
||||
getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let selected: string = ''
|
||||
const { componentControl } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
let selected: string = tabs[0]
|
||||
let noPadding: boolean | undefined = undefined
|
||||
|
||||
$: selectedIndex = tabs?.indexOf(selected) ?? -1
|
||||
|
||||
$: if ((tabs && selected === '') || !tabs.includes(selected)) {
|
||||
selected = tabs[0]
|
||||
}
|
||||
|
||||
$: outputs = $worldStore?.outputsById[id] as {
|
||||
selectedTabIndex: Output<number | null>
|
||||
}
|
||||
|
||||
$: outputs?.selectedTabIndex && handleOutputs()
|
||||
let tabHeight: number = 0
|
||||
|
||||
function handleTabSelection() {
|
||||
outputs?.selectedTabIndex.set(selectedIndex)
|
||||
const selectedIndex = tabs?.indexOf(selected)
|
||||
outputs?.selectedTabIndex.set(selectedIndex, true)
|
||||
|
||||
if ($selectedComponent != id) {
|
||||
$selectedComponent = id
|
||||
}
|
||||
|
||||
if ($focusedGrid?.parentComponentId != id || $focusedGrid?.subGridIndex != selectedIndex) {
|
||||
$focusedGrid = {
|
||||
parentComponentId: id,
|
||||
@@ -48,24 +43,46 @@
|
||||
}
|
||||
}
|
||||
|
||||
$: selectedIndex >= 0 && handleTabSelection()
|
||||
$: $selectedComponent === id && focusGrid()
|
||||
|
||||
let tabHeight: number = 0
|
||||
|
||||
function onFocus() {
|
||||
$focusedGrid = {
|
||||
parentComponentId: id,
|
||||
subGridIndex: selectedIndex ?? 0
|
||||
function focusGrid() {
|
||||
const selectedIndex = tabs?.indexOf(selected)
|
||||
if ($focusedGrid?.parentComponentId != id || $focusedGrid?.subGridIndex != selectedIndex) {
|
||||
$focusedGrid = {
|
||||
parentComponentId: id,
|
||||
subGridIndex: selectedIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$: $selectedComponent === id && selectedIndex >= 0 && onFocus()
|
||||
$componentControl[id] = {
|
||||
left: () => {
|
||||
const index = tabs.indexOf(selected)
|
||||
if (index > 0) {
|
||||
selected = tabs[index - 1]
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
right: () => {
|
||||
const index = tabs.indexOf(selected)
|
||||
if (index < tabs.length - 1) {
|
||||
selected = tabs[index + 1]
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
$: selected && handleTabSelection()
|
||||
|
||||
$: outputs = $worldStore?.outputsById[id] as {
|
||||
selectedTabIndex: Output<number>
|
||||
}
|
||||
|
||||
$: selectedIndex = tabs?.indexOf(selected) ?? -1
|
||||
|
||||
$: css = concatCustomCss($app.css?.tabscomponent, customCss)
|
||||
|
||||
function handleOutputs() {
|
||||
outputs?.selectedTabIndex.set(outputs.selectedTabIndex.peak())
|
||||
}
|
||||
</script>
|
||||
|
||||
<InputValue {id} input={configuration.noPadding} bind:value={noPadding} />
|
||||
@@ -90,7 +107,7 @@
|
||||
{#each tabs ?? [] as res, i}
|
||||
<SubGridEditor
|
||||
{id}
|
||||
visible={i === selectedIndex}
|
||||
visible={render && i === selectedIndex}
|
||||
bind:subGrid={$app.subgrids[`${id}-${i}`]}
|
||||
{noPadding}
|
||||
class={css?.container?.class}
|
||||
|
||||
@@ -4,11 +4,12 @@
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
import { Pane, Splitpanes } from 'svelte-splitpanes'
|
||||
import { writable } from 'svelte/store'
|
||||
import { writable, type Writable } from 'svelte/store'
|
||||
import { buildWorld, type World } from '../rx'
|
||||
import type {
|
||||
App,
|
||||
AppEditorContext,
|
||||
AppViewerContext,
|
||||
ConnectingInput,
|
||||
EditorBreakpoint,
|
||||
EditorMode,
|
||||
@@ -32,11 +33,14 @@
|
||||
|
||||
import SettingsPanel from './SettingsPanel.svelte'
|
||||
import { fly } from 'svelte/transition'
|
||||
import type { Policy } from '$lib/gen'
|
||||
import UnsavedConfirmationModal from '$lib/components/common/confirmationModal/UnsavedConfirmationModal.svelte'
|
||||
import { VariableService, type Policy } from '$lib/gen'
|
||||
import { page } from '$app/stores'
|
||||
import CssSettings from './componentsPanel/CssSettings.svelte'
|
||||
import { initHistory } from '$lib/history'
|
||||
import { Tour } from '../../tutorial'
|
||||
import ComponentNavigation from './component/ComponentNavigation.svelte'
|
||||
import ItemPicker from '$lib/components/ItemPicker.svelte'
|
||||
import VariableEditor from '$lib/components/VariableEditor.svelte'
|
||||
|
||||
export let app: App
|
||||
export let path: string
|
||||
@@ -44,6 +48,7 @@
|
||||
export let policy: Policy
|
||||
export let summary: string
|
||||
export let fromHub: boolean = false
|
||||
export let tour: boolean = false
|
||||
|
||||
const appStore = writable<App>(app)
|
||||
const worldStore = writable<World | undefined>(undefined)
|
||||
@@ -62,8 +67,9 @@
|
||||
const runnableComponents = writable<Record<string, () => Promise<void>>>({})
|
||||
const errorByComponent = writable<Record<string, { error: string; componentId: string }>>({})
|
||||
const focusedGrid = writable<FocusedGrid | undefined>(undefined)
|
||||
const pickVariableCallback: Writable<((path: string) => void) | undefined> = writable(undefined)
|
||||
|
||||
setContext<AppEditorContext>('AppEditorContext', {
|
||||
setContext<AppViewerContext>('AppViewerContext', {
|
||||
worldStore,
|
||||
staticOutputs,
|
||||
app: appStore,
|
||||
@@ -84,8 +90,13 @@
|
||||
openDebugRun: writable(undefined),
|
||||
focusedGrid,
|
||||
stateId: writable(0),
|
||||
parentWidth: writable(0),
|
||||
history
|
||||
parentWidth: writable(0)
|
||||
})
|
||||
|
||||
setContext<AppEditorContext>('AppEditorContext', {
|
||||
history,
|
||||
componentControl: writable({}),
|
||||
pickVariableCallback
|
||||
})
|
||||
|
||||
let timeout: NodeJS.Timeout | undefined = undefined
|
||||
@@ -109,10 +120,16 @@
|
||||
mounted = true
|
||||
})
|
||||
|
||||
$: context = {
|
||||
let context = {
|
||||
email: $userStore?.email,
|
||||
username: $userStore?.username,
|
||||
query: Object.fromEntries($page.url.searchParams.entries())
|
||||
query: Object.fromEntries($page.url.searchParams.entries()),
|
||||
hash: $page.url.hash
|
||||
}
|
||||
|
||||
function hashchange(e: HashChangeEvent) {
|
||||
context.hash = e.newURL.split('#')[1]
|
||||
context = context
|
||||
}
|
||||
|
||||
$: mounted && ($worldStore = buildWorld($staticOutputs, $worldStore, context))
|
||||
@@ -125,15 +142,28 @@
|
||||
} else {
|
||||
selectedTab = 'insert'
|
||||
}
|
||||
|
||||
let itemPicker: ItemPicker | undefined = undefined
|
||||
|
||||
$: if ($pickVariableCallback) {
|
||||
itemPicker?.openDrawer()
|
||||
}
|
||||
|
||||
let variableEditor: VariableEditor | undefined = undefined
|
||||
</script>
|
||||
|
||||
<svelte:window on:hashchange={hashchange} />
|
||||
|
||||
{#if tour}
|
||||
<Tour tutorial="app" />
|
||||
{/if}
|
||||
|
||||
{#if $connectingInput.opened}
|
||||
<div
|
||||
class="absolute w-full h-screen bg-black border-2 bg-opacity-25 z-20 flex justify-center items-center"
|
||||
/>
|
||||
{/if}
|
||||
{#if !$userStore?.operator}
|
||||
<UnsavedConfirmationModal />
|
||||
{#if initialMode !== 'preview'}
|
||||
<AppEditorHeader {policy} {fromHub} />
|
||||
{/if}
|
||||
@@ -172,6 +202,7 @@
|
||||
$selectedComponent = undefined
|
||||
$focusedGrid = undefined
|
||||
}}
|
||||
id="app-tutorial-3"
|
||||
class={twMerge(
|
||||
'bg-gray-100 h-full w-full',
|
||||
$appStore.css?.['app']?.['viewer']?.class
|
||||
@@ -185,6 +216,8 @@
|
||||
)}
|
||||
>
|
||||
{#if $appStore.grid}
|
||||
<ComponentNavigation />
|
||||
|
||||
<div on:pointerdown|stopPropagation class={width}>
|
||||
<GridEditor {policy} />
|
||||
</div>
|
||||
@@ -195,7 +228,7 @@
|
||||
</div>
|
||||
</Pane>
|
||||
<Pane size={$connectingInput?.opened ? 0 : 30}>
|
||||
<div class="relative h-full w-full">
|
||||
<div id="app-tutorial-4" class="relative h-full w-full">
|
||||
<InlineScriptsPanel />
|
||||
</div>
|
||||
</Pane>
|
||||
@@ -204,7 +237,7 @@
|
||||
</Pane>
|
||||
<Pane size={21} minSize={5} maxSize={33}>
|
||||
<div class="relative flex flex-col h-full">
|
||||
<Tabs bind:selected={selectedTab} class="!border-b-2 !border-gray-200 !h-full">
|
||||
<Tabs bind:selected={selectedTab} wrapperClass="!h-[40px]" class="!h-full">
|
||||
<Tab value="insert" size="xs">
|
||||
<div class="m-1 center-center gap-2">
|
||||
<Icon data={faPlus} />
|
||||
@@ -274,3 +307,36 @@
|
||||
{:else}
|
||||
App editor not available to operators
|
||||
{/if}
|
||||
|
||||
<ItemPicker
|
||||
bind:this={itemPicker}
|
||||
pickCallback={(path, _) => {
|
||||
$pickVariableCallback?.(path)
|
||||
}}
|
||||
itemName="Variable"
|
||||
extraField="path"
|
||||
loadItems={async () =>
|
||||
(await VariableService.listVariable({ workspace: $workspaceStore ?? '' })).map((x) => ({
|
||||
name: x.path,
|
||||
...x
|
||||
}))}
|
||||
>
|
||||
<div
|
||||
slot="submission"
|
||||
class="flex flex-row-reverse w-full bg-white border-t border-gray-200 rounded-bl-lg rounded-br-lg"
|
||||
>
|
||||
<Button
|
||||
variant="border"
|
||||
color="blue"
|
||||
size="sm"
|
||||
startIcon={{ icon: faPlus }}
|
||||
on:click={() => {
|
||||
variableEditor?.initNew?.()
|
||||
}}
|
||||
>
|
||||
New variable
|
||||
</Button>
|
||||
</div>
|
||||
</ItemPicker>
|
||||
|
||||
<VariableEditor bind:this={variableEditor} />
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
ButtonPopup,
|
||||
ButtonPopupItem,
|
||||
Drawer,
|
||||
DrawerContent
|
||||
DrawerContent,
|
||||
UndoRedo
|
||||
} from '$lib/components/common'
|
||||
import Button from '$lib/components/common/button/Button.svelte'
|
||||
import { dirtyStore } from '$lib/components/common/confirmationModal/dirtyStore'
|
||||
@@ -26,7 +27,7 @@
|
||||
import Tooltip from '$lib/components/Tooltip.svelte'
|
||||
import { AppService, Job, Policy } from '$lib/gen'
|
||||
import { redo, undo } from '$lib/history'
|
||||
import { userStore, workspaceStore } from '$lib/stores'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import {
|
||||
faBug,
|
||||
faClipboard,
|
||||
@@ -60,7 +61,7 @@
|
||||
StaticAppInput,
|
||||
UserAppInput
|
||||
} from '../inputType'
|
||||
import type { AppEditorContext } from '../types'
|
||||
import type { AppEditorContext, AppViewerContext } from '../types'
|
||||
import { allItems, toStatic } from '../utils'
|
||||
import AppExportButton from './AppExportButton.svelte'
|
||||
import AppInputs from './AppInputs.svelte'
|
||||
@@ -89,9 +90,10 @@
|
||||
errorByComponent,
|
||||
openDebugRun,
|
||||
focusedGrid,
|
||||
selectedComponent,
|
||||
history
|
||||
} = getContext<AppEditorContext>('AppEditorContext')
|
||||
selectedComponent
|
||||
} = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
const { history } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
const loading = {
|
||||
publish: false,
|
||||
@@ -159,7 +161,7 @@
|
||||
return []
|
||||
})
|
||||
.concat(
|
||||
Object.values($app.hiddenInlineScripts).map(async (v) => {
|
||||
Object.values($app.hiddenInlineScripts ?? {}).map(async (v) => {
|
||||
let hex = await hash(v.inlineScript?.content)
|
||||
const staticInputs = collectStaticFields(v.fields)
|
||||
return [`rawscript/${hex}`, staticInputs]
|
||||
@@ -167,9 +169,6 @@
|
||||
)
|
||||
)
|
||||
policy.triggerables = Object.fromEntries(allTriggers.filter((x) => x.length > 0))
|
||||
|
||||
policy.on_behalf_of = `u/${$userStore?.username}`
|
||||
policy.on_behalf_of_email = $userStore?.email
|
||||
}
|
||||
async function createApp(path: string) {
|
||||
await computeTriggerables()
|
||||
@@ -242,8 +241,51 @@
|
||||
|
||||
$: selectedJobId && testJobLoader?.watchJob(selectedJobId)
|
||||
$: hasErrors = Object.keys($errorByComponent).length > 0
|
||||
|
||||
function onKeyDown(event: KeyboardEvent) {
|
||||
switch (event.key) {
|
||||
case 'Z':
|
||||
if (event.ctrlKey) {
|
||||
$app = redo(history)
|
||||
event.preventDefault()
|
||||
}
|
||||
break
|
||||
case 'z':
|
||||
if (event.ctrlKey) {
|
||||
$app = undo(history, $app)
|
||||
event.preventDefault()
|
||||
}
|
||||
break
|
||||
case 's':
|
||||
if (event.ctrlKey) {
|
||||
save()
|
||||
event.preventDefault()
|
||||
}
|
||||
break
|
||||
// case 'ArrowDown': {
|
||||
// let ids = generateIds()
|
||||
// let idx = ids.indexOf($selectedIdStore)
|
||||
// if (idx > -1 && idx < ids.length - 1) {
|
||||
// $selectedIdStore = ids[idx + 1]
|
||||
// event.preventDefault()
|
||||
// }
|
||||
// break
|
||||
// }
|
||||
// case 'ArrowUp': {
|
||||
// let ids = generateIds()
|
||||
// let idx = ids.indexOf($selectedIdStore)
|
||||
// if (idx > 0 && idx < ids.length) {
|
||||
// $selectedIdStore = ids[idx - 1]
|
||||
// event.preventDefault()
|
||||
// }
|
||||
// break
|
||||
// }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={onKeyDown} />
|
||||
|
||||
<TestJobLoader bind:this={testJobLoader} bind:isLoading={testIsLoading} bind:job />
|
||||
|
||||
<Drawer bind:open={saveDrawerOpen} size="800px">
|
||||
@@ -428,32 +470,16 @@
|
||||
<div class="min-w-64 w-64">
|
||||
<input type="text" placeholder="App summary" class="text-sm w-full" bind:value={$summary} />
|
||||
</div>
|
||||
<div class="flex gap-1">
|
||||
<Button
|
||||
title="Undo"
|
||||
disabled={$history.index == 0}
|
||||
variant="border"
|
||||
color="dark"
|
||||
size="xs"
|
||||
on:click={async () => {
|
||||
$app = undo(history, $app)
|
||||
}}
|
||||
>
|
||||
<Undo size={14} />
|
||||
</Button>
|
||||
<Button
|
||||
title="Redo"
|
||||
disabled={$history.index == $history.history.length - 1}
|
||||
variant="border"
|
||||
color="dark"
|
||||
size="xs"
|
||||
on:click={async () => {
|
||||
$app = redo(history)
|
||||
}}
|
||||
>
|
||||
<Redo size={14} />
|
||||
</Button>
|
||||
</div>
|
||||
<UndoRedo
|
||||
undoProps={{ disabled: $history.index === 0 }}
|
||||
redoProps={{ disabled: $history.index === $history.history.length - 1 }}
|
||||
on:undo={() => {
|
||||
$app = undo(history, $app)
|
||||
}}
|
||||
on:redo={() => {
|
||||
$app = redo(history)
|
||||
}}
|
||||
/>
|
||||
<div class="flex gap-4 items-center grow justify-center">
|
||||
<div>
|
||||
<ToggleButtonGroup bind:selected={$mode}>
|
||||
@@ -486,7 +512,7 @@
|
||||
<ToggleButton position="left" value={false} size="xs">
|
||||
<div class="flex gap-1 justify-start items-center">
|
||||
<AlignHorizontalSpaceAround size={14} />
|
||||
<Tooltip light>
|
||||
<Tooltip light class="mb-0.5">
|
||||
The max width is 1168px and the content stay centered instead of taking the full page
|
||||
width
|
||||
</Tooltip>
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
import { Alert } from '$lib/components/common'
|
||||
import Toggle from '$lib/components/Toggle.svelte'
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppEditorContext } from '../types'
|
||||
import type { AppViewerContext } from '../types'
|
||||
import AppComponentInput from './AppComponentInput.svelte'
|
||||
import InputsSpecsEditor from './settingsPanel/InputsSpecsEditor.svelte'
|
||||
|
||||
const { app } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let resourceOnly: boolean = true
|
||||
</script>
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { onMount, setContext } from 'svelte'
|
||||
import { fade } from 'svelte/transition'
|
||||
import { cubicOut } from 'svelte/easing'
|
||||
import { writable, type Writable } from 'svelte/store'
|
||||
import { buildWorld, type World } from '../rx'
|
||||
import type {
|
||||
App,
|
||||
AppEditorContext,
|
||||
AppViewerContext,
|
||||
ConnectingInput,
|
||||
EditorBreakpoint,
|
||||
EditorMode
|
||||
@@ -42,7 +40,7 @@
|
||||
|
||||
const runnableComponents = writable<Record<string, () => Promise<void>>>({})
|
||||
|
||||
setContext<AppEditorContext>('AppEditorContext', {
|
||||
setContext<AppViewerContext>('AppViewerContext', {
|
||||
worldStore,
|
||||
staticOutputs,
|
||||
app: appStore,
|
||||
@@ -63,8 +61,7 @@
|
||||
openDebugRun: writable(undefined),
|
||||
focusedGrid: writable(undefined),
|
||||
stateId: writable(0),
|
||||
parentWidth: writable(0),
|
||||
history: writable<any>(undefined)
|
||||
parentWidth: writable(0)
|
||||
})
|
||||
|
||||
let mounted = false
|
||||
@@ -73,11 +70,20 @@
|
||||
mounted = true
|
||||
})
|
||||
|
||||
$: mounted && ($worldStore = buildWorld($staticOutputs, $worldStore, context))
|
||||
let ncontext = context
|
||||
|
||||
function hashchange(e: HashChangeEvent) {
|
||||
ncontext.hash = e.newURL.split('#')[1]
|
||||
ncontext = ncontext
|
||||
}
|
||||
|
||||
$: mounted && ($worldStore = buildWorld($staticOutputs, $worldStore, ncontext))
|
||||
$: width = $breakpoint === 'sm' ? 'max-w-[640px]' : 'w-full '
|
||||
$: lockedClasses = isLocked ? '!max-h-[400px] overflow-hidden pointer-events-none' : ''
|
||||
</script>
|
||||
|
||||
<svelte:window on:hashchange={hashchange} />
|
||||
|
||||
<div id="app-editor-top-level-drawer" />
|
||||
|
||||
<div class="relative">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { classNames } from '$lib/utils'
|
||||
import type { AppEditorContext } from '../types'
|
||||
import { Anchor, Bug, Move } from 'lucide-svelte'
|
||||
import type { AppViewerContext } from '../types'
|
||||
import { Anchor, Bug, Expand, Move } from 'lucide-svelte'
|
||||
import { createEventDispatcher, getContext } from 'svelte'
|
||||
import Popover from '$lib/components/Popover.svelte'
|
||||
import { Alert, Button } from '$lib/components/common'
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const { errorByComponent, openDebugRun } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { errorByComponent, openDebugRun } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
$: error = Object.values($errorByComponent).find((e) => e.componentId === component.id)
|
||||
|
||||
@@ -55,6 +55,17 @@
|
||||
<Anchor aria-label="Lock position" size={14} />
|
||||
{/if}
|
||||
</button>
|
||||
<button
|
||||
title="Position locking"
|
||||
class={classNames(
|
||||
'text-gray-800 px-1 text-2xs py-0.5 font-bold w-fit shadow border border-gray-300 absolute -top-1 right-[0.5rem] z-50 cursor-pointer',
|
||||
' hover:bg-gray-300',
|
||||
selected ? 'bg-gray-200/80' : 'bg-gray-200/80'
|
||||
)}
|
||||
on:click={() => dispatch('expand')}
|
||||
>
|
||||
<Expand aria-label="Lock position" size={14} />
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
{#if selected || hover}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { getContext, afterUpdate } from 'svelte'
|
||||
import type { AppEditorContext, GridItem } from '../types'
|
||||
import type { App, AppEditorContext, AppViewerContext, GridItem } from '../types'
|
||||
import Grid from '@windmill-labs/svelte-grid'
|
||||
import { classNames } from '$lib/utils'
|
||||
import { columnConfiguration, disableDrag, enableDrag, isFixed, toggleFixed } from '../gridUtils'
|
||||
@@ -10,6 +10,9 @@
|
||||
import type { Policy } from '$lib/gen'
|
||||
import HiddenComponent from '../components/helpers/HiddenComponent.svelte'
|
||||
import Component from './component/Component.svelte'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import { push } from '$lib/history'
|
||||
import { expandGriditem, findGridItem } from './appUtils'
|
||||
|
||||
export let policy: Policy
|
||||
|
||||
@@ -22,8 +25,11 @@
|
||||
runnableComponents,
|
||||
summary,
|
||||
focusedGrid,
|
||||
parentWidth
|
||||
} = getContext<AppEditorContext>('AppEditorContext')
|
||||
parentWidth,
|
||||
breakpoint
|
||||
} = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
const { history } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
// The drag is disabled when the user is connecting an input
|
||||
// or when the user is previewing the app
|
||||
@@ -100,11 +106,16 @@
|
||||
}
|
||||
|
||||
let pointerdown = false
|
||||
let lastapp: App | undefined = undefined
|
||||
const onpointerdown = () => {
|
||||
lastapp = JSON.parse(JSON.stringify($app))
|
||||
pointerdown = true
|
||||
}
|
||||
const onpointerup = () => {
|
||||
pointerdown = false
|
||||
if (!deepEqual(lastapp, $app)) {
|
||||
push(history, lastapp, false, true)
|
||||
}
|
||||
}
|
||||
|
||||
afterUpdate(() => {
|
||||
@@ -169,13 +180,14 @@
|
||||
<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"
|
||||
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"
|
||||
>{dataItem.data.id}</div
|
||||
>
|
||||
{dataItem.data.id}
|
||||
</div>
|
||||
{/if}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
@@ -188,17 +200,23 @@
|
||||
)}
|
||||
>
|
||||
<Component
|
||||
render={true}
|
||||
{pointerdown}
|
||||
component={gridComponent.data}
|
||||
selected={$selectedComponent === dataItem.data.id}
|
||||
locked={isFixed(gridComponent)}
|
||||
on:delete={() => removeGridElement(gridComponent.data)}
|
||||
on:lock={() => {
|
||||
let fComponent = $app.grid.find((c) => c.id === gridComponent.id)
|
||||
if (fComponent) {
|
||||
fComponent = toggleFixed(fComponent)
|
||||
const gridItem = findGridItem($app, gridComponent.data.id)
|
||||
if (gridItem) {
|
||||
toggleFixed(gridItem)
|
||||
}
|
||||
}}
|
||||
on:expand={() => {
|
||||
push(history, $app)
|
||||
expandGriditem($app.grid, gridComponent, $breakpoint)
|
||||
$app = { ...$app }
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppEditorContext, GridItem } from '../types'
|
||||
import type { AppViewerContext, GridItem } from '../types'
|
||||
import ComponentPanel from './settingsPanel/ComponentPanel.svelte'
|
||||
|
||||
const { selectedComponent } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { selectedComponent } = getContext<AppViewerContext>('AppViewerContext')
|
||||
export let gridItems: GridItem[]
|
||||
export let parent: string | undefined
|
||||
</script>
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
import { ChevronDown, RefreshCw } from 'lucide-svelte'
|
||||
import { getContext, onMount } from 'svelte'
|
||||
import Button from '../../common/button/Button.svelte'
|
||||
import type { AppEditorContext } from '../types'
|
||||
import type { AppViewerContext } from '../types'
|
||||
|
||||
const { runnableComponents } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { runnableComponents } = getContext<AppViewerContext>('AppViewerContext')
|
||||
let loading: boolean = false
|
||||
let timeout: NodeJS.Timer | undefined = undefined
|
||||
let interval: number | undefined = undefined
|
||||
@@ -14,14 +14,14 @@
|
||||
$: componentNumber = Object.keys($runnableComponents).length
|
||||
|
||||
function onClick(stopAfterClear = true) {
|
||||
if(timeout) {
|
||||
if (timeout) {
|
||||
clearInterval(timeout)
|
||||
timeout = undefined
|
||||
shouldRefresh = false
|
||||
if(stopAfterClear) return;
|
||||
if (stopAfterClear) return
|
||||
}
|
||||
refresh()
|
||||
if(interval) {
|
||||
if (interval) {
|
||||
shouldRefresh = true
|
||||
timeout = setInterval(refresh, interval)
|
||||
}
|
||||
@@ -44,12 +44,12 @@
|
||||
}
|
||||
|
||||
function visChange() {
|
||||
if(document.visibilityState === 'hidden') {
|
||||
if(timeout) {
|
||||
if (document.visibilityState === 'hidden') {
|
||||
if (timeout) {
|
||||
clearInterval(timeout)
|
||||
timeout = undefined
|
||||
}
|
||||
} else if(shouldRefresh) {
|
||||
} else if (shouldRefresh) {
|
||||
timeout = setInterval(refresh, interval)
|
||||
}
|
||||
}
|
||||
@@ -58,7 +58,7 @@
|
||||
document.addEventListener('visibilitychange', visChange)
|
||||
return () => {
|
||||
document.removeEventListener('visibilitychange', visChange)
|
||||
if(timeout) clearInterval(timeout)
|
||||
if (timeout) clearInterval(timeout)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -66,17 +66,19 @@
|
||||
<div class="flex items-center">
|
||||
<Button
|
||||
on:click={() => onClick()}
|
||||
color="{timeout ? 'blue' : 'light'}"
|
||||
variant="{timeout ? 'contained' : 'border'}"
|
||||
color={timeout ? 'blue' : 'light'}
|
||||
variant={timeout ? 'contained' : 'border'}
|
||||
size="xs"
|
||||
btnClasses="!rounded-r-none {timeout ? '!border !border-blue-500' : ''}"
|
||||
title="Refresh {componentNumber} component{componentNumber > 1 ? 's' : ''} {interval ? `every ${interval / 1000} seconds` : 'once'}"
|
||||
title="Refresh {componentNumber} component{componentNumber > 1 ? 's' : ''} {interval
|
||||
? `every ${interval / 1000} seconds`
|
||||
: 'once'}"
|
||||
>
|
||||
<RefreshCw class={loading ? 'animate-spin' : ''} size={16} />
|
||||
</Button>
|
||||
<Dropdown
|
||||
btnClasses="!rounded-l-none !border-l-0 min-w-[4rem] !px-2"
|
||||
color="{timeout ? 'blue' : 'light'}"
|
||||
color={timeout ? 'blue' : 'light'}
|
||||
variant="border"
|
||||
dropdownItems={[
|
||||
{
|
||||
@@ -86,7 +88,7 @@
|
||||
...[1, 2, 3, 4, 5, 6].map((i) => ({
|
||||
displayName: `Every ${i * 5} seconds`,
|
||||
action: () => setInter(i * 5000)
|
||||
})),
|
||||
}))
|
||||
]}
|
||||
>
|
||||
<span class="grow text center">{interval ? `${interval / 1000}s` : 'once'}</span>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppEditorContext } from '../types'
|
||||
import type { AppViewerContext } from '../types'
|
||||
import GridPanel from './GridPanel.svelte'
|
||||
import PanelSection from './settingsPanel/common/PanelSection.svelte'
|
||||
import InputsSpecsEditor from './settingsPanel/InputsSpecsEditor.svelte'
|
||||
|
||||
const { selectedComponent, app, stateId } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { selectedComponent, app, stateId } = getContext<AppViewerContext>('AppViewerContext')
|
||||
</script>
|
||||
|
||||
{#if $app.grid}
|
||||
|
||||
@@ -4,9 +4,10 @@
|
||||
import Grid from '@windmill-labs/svelte-grid'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { columnConfiguration, isFixed, toggleFixed } from '../gridUtils'
|
||||
import type { AppEditorContext, GridItem } from '../types'
|
||||
import type { AppEditorContext, AppViewerContext, GridItem } from '../types'
|
||||
import Component from './component/Component.svelte'
|
||||
import { findGridItem } from './appUtils'
|
||||
import { expandGriditem, findGridItem } from './appUtils'
|
||||
import { push } from '$lib/history'
|
||||
|
||||
export let containerHeight: number
|
||||
let classes = ''
|
||||
@@ -21,8 +22,10 @@
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const { app, connectingInput, selectedComponent, focusedGrid, mode, parentWidth } =
|
||||
getContext<AppEditorContext>('AppEditorContext')
|
||||
const { app, connectingInput, selectedComponent, focusedGrid, mode, parentWidth, breakpoint } =
|
||||
getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
const { history } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
$: highlight = id === $focusedGrid?.parentComponentId && shouldHighlight
|
||||
|
||||
@@ -64,8 +67,6 @@
|
||||
|
||||
// @ts-ignore
|
||||
let container
|
||||
|
||||
// $: containerHeight = subGrid.map((item) => columnConfiguration.map((c) => item[c[1]].h))
|
||||
</script>
|
||||
|
||||
<div
|
||||
@@ -123,11 +124,24 @@
|
||||
)}
|
||||
>
|
||||
<Component
|
||||
render={visible}
|
||||
{pointerdown}
|
||||
component={gridComponent.data}
|
||||
selected={$selectedComponent === dataItem.data.id}
|
||||
locked={isFixed(gridComponent)}
|
||||
on:lock={() => lock(gridComponent)}
|
||||
on:expand={() => {
|
||||
const parentGridItem = findGridItem($app, id)
|
||||
|
||||
if (!parentGridItem) {
|
||||
return
|
||||
}
|
||||
|
||||
push(history, $app)
|
||||
|
||||
expandGriditem(subGrid, gridComponent, $breakpoint, parentGridItem)
|
||||
$app = { ...$app }
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { getNextId } from '$lib/components/flows/flowStateUtils'
|
||||
import type { App, FocusedGrid, GridItem } from '../types'
|
||||
import type { App, EditorBreakpoint, FocusedGrid, GridItem } from '../types'
|
||||
import { getRecommendedDimensionsByComponent, type AppComponent } from './component'
|
||||
import gridHelp from '@windmill-labs/svelte-grid/src/utils/helper'
|
||||
import { gridColumns } from '../gridUtils'
|
||||
@@ -18,6 +18,21 @@ function findGridItemById(
|
||||
return undefined
|
||||
}
|
||||
|
||||
export function findGridItemParentId(app: App, id: string): string | undefined {
|
||||
const gridItem = app.grid.find((x) => x.id === id)
|
||||
if (gridItem) {
|
||||
return undefined
|
||||
} else {
|
||||
for (const key in app.subgrids) {
|
||||
const subGrid = app.subgrids[key]
|
||||
const gridItem = subGrid.find((x) => x.id === id)
|
||||
if (gridItem) {
|
||||
return key.split('-')[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function findGridItem(app: App, id: string): GridItem | undefined {
|
||||
return findGridItemById(app.grid, app.subgrids, id)
|
||||
}
|
||||
@@ -31,7 +46,6 @@ export function getNextGridItemId(app: App): string {
|
||||
}
|
||||
|
||||
export function createNewGridItem(grid: GridItem[], id: string, data: AppComponent): GridItem {
|
||||
|
||||
const appComponent = data
|
||||
|
||||
appComponent.id = id
|
||||
@@ -176,3 +190,131 @@ export function duplicateGridItem(
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
type AvailableSpace = {
|
||||
left: number
|
||||
right: number
|
||||
top: number
|
||||
bottom: number
|
||||
}
|
||||
|
||||
export function findAvailableSpace(
|
||||
grid: GridItem[],
|
||||
gridItem: GridItem,
|
||||
editorBreakpoint: EditorBreakpoint,
|
||||
parentGridItem: GridItem | undefined = undefined
|
||||
): AvailableSpace | undefined {
|
||||
if (gridItem) {
|
||||
const breakpoint = editorBreakpoint === 'sm' ? 3 : 12
|
||||
const maxHeight = parentGridItem ? parentGridItem[breakpoint].h - 1 : 12
|
||||
const maxWidth = 12
|
||||
|
||||
const availableSpace = {
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 0,
|
||||
bottom: 0
|
||||
}
|
||||
|
||||
const items = grid.map((item) => {
|
||||
return {
|
||||
id: item.id,
|
||||
x: item[breakpoint].x,
|
||||
y: item[breakpoint].y,
|
||||
w: item[breakpoint].w,
|
||||
h: item[breakpoint].h
|
||||
}
|
||||
})
|
||||
|
||||
const item = items.find((item) => item.id === gridItem.id)
|
||||
|
||||
if (!item) {
|
||||
return availableSpace
|
||||
}
|
||||
|
||||
if (item.x > 0) {
|
||||
for (let x = item.x - 1; x >= 0; x--) {
|
||||
const itemToCheck = { ...item, x, w: 1 }
|
||||
const isItemInWay = items.some((item) => isOverlapping(item, itemToCheck))
|
||||
|
||||
if (isItemInWay) {
|
||||
break
|
||||
} else {
|
||||
availableSpace.left++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (item.x + item.w < maxWidth) {
|
||||
for (let x = item.x + item.w; x < maxWidth; x++) {
|
||||
const itemToCheck = { ...item, x, w: 1 }
|
||||
const isItemInWay = items.some((item) => isOverlapping(item, itemToCheck))
|
||||
|
||||
if (isItemInWay) {
|
||||
break
|
||||
} else {
|
||||
availableSpace.right++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (item.y > 0) {
|
||||
for (let y = item.y - 1; y >= 0; y--) {
|
||||
const itemToCheck = { ...item, h: 1, y }
|
||||
const isItemInWay = items.some((item) => isOverlapping(item, itemToCheck))
|
||||
|
||||
if (isItemInWay) {
|
||||
break
|
||||
} else {
|
||||
availableSpace.top++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (item.y + item.h < maxHeight) {
|
||||
for (let y = item.y + item.h; y < maxHeight; y++) {
|
||||
const itemToCheck = { ...item, h: 1, y }
|
||||
const isItemInWay = items.some((item) => isOverlapping(item, itemToCheck))
|
||||
|
||||
if (isItemInWay) {
|
||||
break
|
||||
} else {
|
||||
availableSpace.bottom++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return availableSpace
|
||||
}
|
||||
}
|
||||
|
||||
function isOverlapping(item1: any, item2: any) {
|
||||
return (
|
||||
item1.x < item2.x + item2.w &&
|
||||
item1.x + item1.w > item2.x &&
|
||||
item1.y < item2.y + item2.h &&
|
||||
item1.y + item1.h > item2.y
|
||||
)
|
||||
}
|
||||
|
||||
export function expandGriditem(
|
||||
grid: GridItem[],
|
||||
gridComponent: GridItem,
|
||||
$breakpoint: EditorBreakpoint,
|
||||
parentGridItem: GridItem | undefined = undefined
|
||||
) {
|
||||
const availableSpace = findAvailableSpace(grid, gridComponent, $breakpoint, parentGridItem)
|
||||
|
||||
if (!availableSpace) {
|
||||
return
|
||||
}
|
||||
|
||||
const { left, right, top, bottom } = availableSpace
|
||||
const width = $breakpoint === 'sm' ? 3 : 12
|
||||
const previousGridItem = JSON.parse(JSON.stringify(gridComponent[width]))
|
||||
|
||||
gridComponent[width].x = previousGridItem.x - left
|
||||
gridComponent[width].y = previousGridItem.y - top
|
||||
gridComponent[width].w = previousGridItem.w + left + right
|
||||
gridComponent[width].h = previousGridItem.h + top + bottom
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { fade } from 'svelte/transition'
|
||||
import { Loader2 } from 'lucide-svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import type { AppEditorContext } from '../../types'
|
||||
import type { AppViewerContext } from '../../types'
|
||||
import ComponentHeader from '../ComponentHeader.svelte'
|
||||
import type { AppComponent } from './components'
|
||||
import {
|
||||
@@ -46,9 +46,10 @@
|
||||
export let selected: boolean
|
||||
export let locked: boolean = false
|
||||
export let pointerdown: boolean = false
|
||||
export let render: boolean
|
||||
|
||||
const { staticOutputs, mode, connectingInput, app, errorByComponent } =
|
||||
getContext<AppEditorContext>('AppEditorContext')
|
||||
getContext<AppViewerContext>('AppViewerContext')
|
||||
let hover = false
|
||||
let initializing: boolean | undefined = undefined
|
||||
let componentContainerHeight: number = 0
|
||||
@@ -68,7 +69,16 @@
|
||||
class="h-full flex flex-col w-full component"
|
||||
>
|
||||
{#if $mode !== 'preview'}
|
||||
<ComponentHeader {hover} {pointerdown} {component} {selected} on:delete on:lock {locked} />
|
||||
<ComponentHeader
|
||||
{hover}
|
||||
{pointerdown}
|
||||
{component}
|
||||
{selected}
|
||||
on:delete
|
||||
on:lock
|
||||
on:expand
|
||||
{locked}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<div
|
||||
@@ -100,6 +110,7 @@
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'barchartcomponent'}
|
||||
<AppBarChart
|
||||
@@ -109,6 +120,7 @@
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'timeseriescomponent'}
|
||||
<AppTimeseries
|
||||
@@ -118,6 +130,7 @@
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'htmlcomponent'}
|
||||
<AppHtml
|
||||
@@ -126,6 +139,7 @@
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'vegalitecomponent'}
|
||||
<VegaLiteHtml
|
||||
@@ -134,6 +148,7 @@
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'plotlycomponent'}
|
||||
<PlotlyHtml
|
||||
@@ -141,6 +156,7 @@
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'scatterchartcomponent'}
|
||||
<AppScatterChart
|
||||
@@ -150,6 +166,7 @@
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'piechartcomponent'}
|
||||
<AppPieChart
|
||||
@@ -159,6 +176,7 @@
|
||||
bind:initializing
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
componentInput={component.componentInput}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'tablecomponent'}
|
||||
<AppTable
|
||||
@@ -169,6 +187,7 @@
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
componentInput={component.componentInput}
|
||||
bind:actionButtons={component.actionButtons}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'aggridcomponent'}
|
||||
<AppAggridTable
|
||||
@@ -177,6 +196,7 @@
|
||||
bind:initializing
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
componentInput={component.componentInput}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'textcomponent'}
|
||||
<AppText
|
||||
@@ -188,6 +208,7 @@
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'buttoncomponent'}
|
||||
<AppButton
|
||||
@@ -199,6 +220,7 @@
|
||||
componentInput={component.componentInput}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
recomputeIds={component.recomputeIds}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'selectcomponent'}
|
||||
<AppSelect
|
||||
@@ -208,6 +230,7 @@
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'multiselectcomponent'}
|
||||
<AppMultiSelect
|
||||
@@ -217,6 +240,7 @@
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'formcomponent'}
|
||||
<AppForm
|
||||
@@ -227,6 +251,7 @@
|
||||
componentInput={component.componentInput}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
recomputeIds={component.recomputeIds}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'formbuttoncomponent'}
|
||||
<AppFormButton
|
||||
@@ -238,6 +263,7 @@
|
||||
componentInput={component.componentInput}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
recomputeIds={component.recomputeIds}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'checkboxcomponent'}
|
||||
<AppCheckbox
|
||||
@@ -247,6 +273,7 @@
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'textinputcomponent'}
|
||||
<AppTextInput
|
||||
@@ -255,6 +282,18 @@
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'emailinputcomponent'}
|
||||
<AppTextInput
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
configuration={component.configuration}
|
||||
inputType="email"
|
||||
appCssKey="emailinputcomponent"
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'passwordinputcomponent'}
|
||||
<AppTextInput
|
||||
@@ -265,6 +304,7 @@
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'dateinputcomponent'}
|
||||
<AppDateInput
|
||||
@@ -274,6 +314,7 @@
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'numberinputcomponent'}
|
||||
<AppNumberInput
|
||||
@@ -282,6 +323,7 @@
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'currencycomponent'}
|
||||
<AppCurrencyInput
|
||||
@@ -290,6 +332,7 @@
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'slidercomponent'}
|
||||
<AppSliderInputs
|
||||
@@ -298,6 +341,7 @@
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'horizontaldividercomponent'}
|
||||
<AppDivider
|
||||
@@ -307,6 +351,7 @@
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
position="horizontal"
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'verticaldividercomponent'}
|
||||
<AppDivider
|
||||
@@ -316,6 +361,7 @@
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
position="vertical"
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'rangecomponent'}
|
||||
<AppRangeInput
|
||||
@@ -324,6 +370,7 @@
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'tabscomponent'}
|
||||
<AppTabs
|
||||
@@ -333,6 +380,7 @@
|
||||
customCss={component.customCss}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
{componentContainerHeight}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'containercomponent'}
|
||||
<AppContainer
|
||||
@@ -341,6 +389,7 @@
|
||||
customCss={component.customCss}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
{componentContainerHeight}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'verticalsplitpanescomponent'}
|
||||
<AppSplitpanes
|
||||
@@ -348,8 +397,8 @@
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
panes={component.panes}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
{componentContainerHeight}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'horizontalsplitpanescomponent'}
|
||||
<AppSplitpanes
|
||||
@@ -357,9 +406,9 @@
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
panes={component.panes}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
{componentContainerHeight}
|
||||
horizontal={true}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'iconcomponent'}
|
||||
<AppIcon
|
||||
@@ -369,6 +418,7 @@
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'fileinputcomponent'}
|
||||
<AppFileInput
|
||||
@@ -376,6 +426,7 @@
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'imagecomponent'}
|
||||
<AppImage
|
||||
@@ -383,6 +434,7 @@
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'drawercomponent'}
|
||||
<AppDrawer
|
||||
@@ -391,6 +443,7 @@
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'mapcomponent'}
|
||||
<AppMap
|
||||
@@ -398,6 +451,7 @@
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'pdfcomponent'}
|
||||
<AppPdf
|
||||
@@ -405,6 +459,7 @@
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
{render}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppEditorContext, AppViewerContext, EditorBreakpoint, GridItem } from '../../types'
|
||||
import { findGridItemParentId } from '../appUtils'
|
||||
|
||||
const { app, selectedComponent, breakpoint, focusedGrid } =
|
||||
getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
const { componentControl } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
function getSortedGridItemsOfChildren(): GridItem[] {
|
||||
if (!$focusedGrid) {
|
||||
return sortGridItems($app.grid, $breakpoint)
|
||||
}
|
||||
|
||||
if (!$app.subgrids) {
|
||||
return []
|
||||
}
|
||||
|
||||
return sortGridItems(
|
||||
$app.subgrids[`${$focusedGrid.parentComponentId}-${$focusedGrid.subGridIndex}`],
|
||||
$breakpoint
|
||||
)
|
||||
}
|
||||
|
||||
function getSortedGridItemsOfGrid(): GridItem[] {
|
||||
if ($app.grid.find((item) => item.id === $selectedComponent)) {
|
||||
return sortGridItems($app.grid, $breakpoint)
|
||||
}
|
||||
|
||||
if (!$app.subgrids) {
|
||||
return []
|
||||
}
|
||||
|
||||
return sortGridItems(
|
||||
Object.values($app.subgrids ?? {}).find((grid) =>
|
||||
grid.find((item) => item.id === $selectedComponent)
|
||||
) ?? [],
|
||||
$breakpoint
|
||||
)
|
||||
}
|
||||
|
||||
function left(event: KeyboardEvent) {
|
||||
if (!$componentControl[$selectedComponent!]?.left?.()) {
|
||||
const sortedGridItems = getSortedGridItemsOfGrid()
|
||||
const currentIndex = sortedGridItems.findIndex((item) => item.id === $selectedComponent)
|
||||
|
||||
if (currentIndex !== -1 && currentIndex > 0) {
|
||||
$selectedComponent = sortedGridItems[currentIndex - 1].id
|
||||
}
|
||||
}
|
||||
|
||||
event.preventDefault()
|
||||
}
|
||||
|
||||
function right(event: KeyboardEvent) {
|
||||
let r = $componentControl[$selectedComponent!]?.right?.()
|
||||
if (!r) {
|
||||
const sortedGridItems = getSortedGridItemsOfGrid()
|
||||
const currentIndex = sortedGridItems.findIndex((item) => item.id === $selectedComponent)
|
||||
|
||||
if (currentIndex !== -1 && currentIndex < sortedGridItems.length - 1) {
|
||||
$selectedComponent = sortedGridItems[currentIndex + 1].id
|
||||
}
|
||||
}
|
||||
|
||||
event.preventDefault()
|
||||
}
|
||||
|
||||
function down(event: KeyboardEvent) {
|
||||
if (!$focusedGrid) {
|
||||
$selectedComponent = getSortedGridItemsOfChildren()[0]?.id
|
||||
event.preventDefault()
|
||||
} else if ($app.subgrids) {
|
||||
const index = $focusedGrid?.subGridIndex ?? 0
|
||||
const subgrid = $app.subgrids[`${$selectedComponent}-${index}`]
|
||||
|
||||
if (!subgrid || subgrid.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
const sortedGridItems = sortGridItems(subgrid, $breakpoint)
|
||||
|
||||
if (sortedGridItems) {
|
||||
$selectedComponent = sortedGridItems[0].id
|
||||
}
|
||||
event.preventDefault()
|
||||
}
|
||||
}
|
||||
|
||||
function keydown(event: KeyboardEvent) {
|
||||
switch (event.key) {
|
||||
case 'Escape':
|
||||
$selectedComponent = undefined
|
||||
event.preventDefault()
|
||||
break
|
||||
|
||||
case 'ArrowUp': {
|
||||
if (!$selectedComponent) return
|
||||
let parentId = findGridItemParentId($app, $selectedComponent)
|
||||
if (parentId) {
|
||||
$selectedComponent = parentId
|
||||
} else {
|
||||
$selectedComponent = undefined
|
||||
$focusedGrid = undefined
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case 'ArrowDown': {
|
||||
down(event)
|
||||
break
|
||||
}
|
||||
|
||||
case 'ArrowRight': {
|
||||
right(event)
|
||||
break
|
||||
}
|
||||
|
||||
case 'ArrowLeft': {
|
||||
left(event)
|
||||
break
|
||||
}
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
function sortGridItems(gridItems: GridItem[], breakpoint: EditorBreakpoint): GridItem[] {
|
||||
return gridItems.sort((a: GridItem, b: GridItem) => {
|
||||
const width = breakpoint === 'lg' ? 12 : 3
|
||||
|
||||
const aX = a[width].x
|
||||
const aY = a[width].y
|
||||
const bX = b[width].x
|
||||
const bY = b[width].y
|
||||
|
||||
if (aY < bY) {
|
||||
return -1
|
||||
} else if (aY > bY) {
|
||||
return 1
|
||||
} else {
|
||||
if (aX < bX) {
|
||||
return -1
|
||||
} else if (aX > bX) {
|
||||
return 1
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={keydown} />
|
||||
@@ -32,7 +32,8 @@ import {
|
||||
MapPin,
|
||||
FlipHorizontal,
|
||||
FlipVertical,
|
||||
FileText
|
||||
FileText,
|
||||
AtSignIcon
|
||||
} from 'lucide-svelte'
|
||||
import type { BaseAppComponent } from '../../types'
|
||||
|
||||
@@ -43,6 +44,7 @@ type BaseComponent<T extends string> = {
|
||||
export type TextComponent = BaseComponent<'textcomponent'>
|
||||
export type TextInputComponent = BaseComponent<'textinputcomponent'>
|
||||
export type PasswordInputComponent = BaseComponent<'passwordinputcomponent'>
|
||||
export type EmailInputComponent = BaseComponent<'emailinputcomponent'>
|
||||
export type DateInputComponent = BaseComponent<'dateinputcomponent'>
|
||||
export type NumberInputComponent = BaseComponent<'numberinputcomponent'>
|
||||
export type CurrencyComponent = BaseComponent<'currencycomponent'>
|
||||
@@ -99,6 +101,7 @@ export type AppComponent = BaseAppComponent &
|
||||
| DisplayComponent
|
||||
| TextInputComponent
|
||||
| PasswordInputComponent
|
||||
| EmailInputComponent
|
||||
| DateInputComponent
|
||||
| NumberInputComponent
|
||||
| CurrencyComponent
|
||||
@@ -1136,6 +1139,35 @@ Hello \${ctx.username}
|
||||
card: false
|
||||
}
|
||||
},
|
||||
emailinputcomponent: {
|
||||
name: 'Email Input',
|
||||
icon: AtSignIcon,
|
||||
dims: '2:1-3:1',
|
||||
data: {
|
||||
softWrap: true,
|
||||
verticalAlignment: 'center',
|
||||
id: '',
|
||||
type: 'emailinputcomponent',
|
||||
componentInput: undefined,
|
||||
configuration: {
|
||||
placeholder: {
|
||||
type: 'static',
|
||||
value: 'Email',
|
||||
fieldType: 'text',
|
||||
onlyStatic: true
|
||||
},
|
||||
defaultValue: {
|
||||
type: 'static',
|
||||
value: undefined,
|
||||
fieldType: 'text'
|
||||
}
|
||||
},
|
||||
customCss: {
|
||||
input: { class: '', style: '' }
|
||||
} as const,
|
||||
card: false
|
||||
}
|
||||
},
|
||||
dateinputcomponent: {
|
||||
name: 'Date',
|
||||
icon: Calendar,
|
||||
@@ -1217,7 +1249,7 @@ Hello \${ctx.username}
|
||||
color: {
|
||||
type: 'static',
|
||||
value: 'currentColor',
|
||||
fieldType: 'text'
|
||||
fieldType: 'color'
|
||||
},
|
||||
size: {
|
||||
type: 'static',
|
||||
@@ -1258,7 +1290,7 @@ Hello \${ctx.username}
|
||||
color: {
|
||||
type: 'static',
|
||||
value: '#00000060',
|
||||
fieldType: 'text',
|
||||
fieldType: 'color',
|
||||
onlyStatic: true
|
||||
}
|
||||
},
|
||||
@@ -1288,7 +1320,7 @@ Hello \${ctx.username}
|
||||
color: {
|
||||
type: 'static',
|
||||
value: '#00000060',
|
||||
fieldType: 'text',
|
||||
fieldType: 'color',
|
||||
onlyStatic: true
|
||||
}
|
||||
},
|
||||
|
||||
@@ -72,14 +72,14 @@ return {
|
||||
},
|
||||
displaycomponent: {
|
||||
deno: `export async function main() {
|
||||
return {
|
||||
"foo": 42
|
||||
}
|
||||
return {
|
||||
"foo": 42
|
||||
}
|
||||
}`,
|
||||
python3: `def main():
|
||||
return {
|
||||
"foo": 42
|
||||
}`
|
||||
return {
|
||||
"foo": 42
|
||||
}`
|
||||
},
|
||||
htmlcomponent: {
|
||||
deno: `export async function main() {
|
||||
|
||||
@@ -23,6 +23,7 @@ const inputs: ComponentSet = {
|
||||
components: [
|
||||
'textinputcomponent',
|
||||
'passwordinputcomponent',
|
||||
'emailinputcomponent',
|
||||
'numberinputcomponent',
|
||||
'currencycomponent',
|
||||
'slidercomponent',
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
<script lang="ts">
|
||||
import type { AppEditorContext } from '../../types'
|
||||
import type { AppEditorContext, AppViewerContext } from '../../types'
|
||||
import { getContext } from 'svelte'
|
||||
import { fade, slide } from 'svelte/transition'
|
||||
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 } from '../appUtils'
|
||||
import { X } from 'lucide-svelte'
|
||||
import { push } from '$lib/history'
|
||||
import { flip } from 'svelte/animate'
|
||||
|
||||
const { app, selectedComponent, focusedGrid, history } =
|
||||
getContext<AppEditorContext>('AppEditorContext')
|
||||
const { app, selectedComponent, focusedGrid } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
const { history } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
function addComponent(appComponentType: AppComponent['type']): void {
|
||||
push(history, $app)
|
||||
@@ -34,7 +37,7 @@
|
||||
}))
|
||||
</script>
|
||||
|
||||
<section class="p-2 sticky bg-white border-b w-full h-12 z-20 top-0">
|
||||
<section class="p-2 sticky bg-white w-full z-20 top-0">
|
||||
<div class="relative">
|
||||
<input
|
||||
bind:value={search}
|
||||
@@ -52,32 +55,45 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{#if componentsFiltered.reduce((acc, { components }) => acc + components.length, 0) === 0}
|
||||
<div class="text-xs text-gray-500 py-1 px-2"> No components found </div>
|
||||
{:else}
|
||||
{#each componentsFiltered as { title, components }, index (index)}
|
||||
<ListItem title={`${title} (${components.length})`}>
|
||||
{#if components.length}
|
||||
<div class="flex flex-wrap gap-2 py-2">
|
||||
{#each components as item}
|
||||
<button
|
||||
on:click={() => addComponent(item)}
|
||||
title={componentsRecord[item].name}
|
||||
class="border w-24 shadow-sm h-16 p-2 flex flex-col gap-2 items-center
|
||||
justify-center bg-white rounded-md hover:bg-gray-100 duration-200"
|
||||
>
|
||||
<svelte:component this={componentsRecord[item].icon} />
|
||||
<div class="text-xs w-full text-center ellipsize">
|
||||
{componentsRecord[item].name}
|
||||
<div class="relative">
|
||||
{#if componentsFiltered.reduce((acc, { components }) => acc + components.length, 0) === 0}
|
||||
<div
|
||||
in:fade|local={{ duration: 50, delay: 50 }}
|
||||
out:fade|local={{ duration: 50 }}
|
||||
class="absolute left-0 top-0 w-full text-sm text-gray-500 text-center py-6 px-2"
|
||||
>
|
||||
No components found
|
||||
</div>
|
||||
{:else}
|
||||
<div
|
||||
id="app-tutorial-1"
|
||||
in:fade|local={{ duration: 50, delay: 50 }}
|
||||
out:fade|local={{ duration: 50 }}
|
||||
>
|
||||
{#each componentsFiltered as { title, components }, index (index)}
|
||||
{#if components.length}
|
||||
<div transition:slide|local={{ duration: 300 }}>
|
||||
<ListItem title={`${title} (${components.length})`}>
|
||||
<div class="flex flex-wrap gap-2 py-2">
|
||||
{#each components as item (item)}
|
||||
<button
|
||||
animate:flip={{ duration: 300 }}
|
||||
on:click={() => addComponent(item)}
|
||||
title={componentsRecord[item].name}
|
||||
class="border w-24 shadow-sm h-16 p-2 flex flex-col gap-2 items-center
|
||||
justify-center bg-white rounded-md hover:bg-gray-100 duration-200"
|
||||
>
|
||||
<svelte:component this={componentsRecord[item].icon} />
|
||||
<div class="text-xs w-full text-center ellipsize">
|
||||
{componentsRecord[item].name}
|
||||
</div>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="text-xs text-gray-500 py-1 px-2">
|
||||
There are no components in this group yet
|
||||
</div>
|
||||
{/if}
|
||||
</ListItem>
|
||||
{/each}
|
||||
{/if}
|
||||
</ListItem>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import SimpleEditor from '$lib/components/SimpleEditor.svelte'
|
||||
import { emptyString } from '$lib/utils'
|
||||
import { Tab, TabContent, Tabs } from '../../../common'
|
||||
import type { AppEditorContext } from '../../types'
|
||||
import type { AppViewerContext } from '../../types'
|
||||
import ListItem from './ListItem.svelte'
|
||||
import { isOpenStore } from './store'
|
||||
import CssProperty from './CssProperty.svelte'
|
||||
@@ -24,7 +24,7 @@
|
||||
ids: string[]
|
||||
}
|
||||
|
||||
const { app } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let rawCode = ''
|
||||
let viewJsonSchema = false
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
import ObjectViewer from '$lib/components/propertyPicker/ObjectViewer.svelte'
|
||||
import { getContext } from 'svelte'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppEditorContext } from '../../types'
|
||||
import type { AppViewerContext } from '../../types'
|
||||
|
||||
export let outputs: string[] = []
|
||||
export let componentId: string
|
||||
|
||||
const { worldStore } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { worldStore } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let object = {}
|
||||
|
||||
|
||||
@@ -2,14 +2,16 @@
|
||||
import { classNames } from '$lib/utils'
|
||||
import { X } from 'lucide-svelte'
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppEditorContext } from '../../types'
|
||||
import { flip } from 'svelte/animate'
|
||||
import { fade } from 'svelte/transition'
|
||||
import type { AppViewerContext } from '../../types'
|
||||
import { findGridItem } from '../appUtils'
|
||||
import { components } from '../component'
|
||||
import PanelSection from '../settingsPanel/common/PanelSection.svelte'
|
||||
import ComponentOutputViewer from './ComponentOutputViewer.svelte'
|
||||
|
||||
const { connectingInput, staticOutputs, worldStore, selectedComponent, app } =
|
||||
getContext<AppEditorContext>('AppEditorContext')
|
||||
getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
function connectInput(componentId: string, path: string) {
|
||||
if ($connectingInput) {
|
||||
@@ -40,7 +42,7 @@
|
||||
return 'Table action'
|
||||
}
|
||||
}
|
||||
$: panels = [['ctx', ['email', 'username', 'query']] as [string, string[]]].concat(
|
||||
$: panels = [['ctx', ['email', 'username', 'query', 'hash']] as [string, string[]]].concat(
|
||||
Object.entries($staticOutputs)
|
||||
)
|
||||
|
||||
@@ -58,83 +60,100 @@
|
||||
</script>
|
||||
|
||||
<PanelSection noPadding titlePadding="px-4 pt-2 pb-0.5" title="Outputs">
|
||||
<div
|
||||
class="overflow-auto min-w-[150px] border-t w-full relative flex flex-col gap-4 px-2 pt-4 pb-2"
|
||||
>
|
||||
<div class="relative {$connectingInput?.opened ? 'bg-white z-50' : ''}">
|
||||
<input
|
||||
bind:value={search}
|
||||
class="px-2 py-1 border border-gray-300 rounded-sm {search ? 'pr-8' : ''}"
|
||||
placeholder="Search outputs..."
|
||||
/>
|
||||
{#if search}
|
||||
<button
|
||||
class="absolute right-2 top-1/2 transform -translate-y-1/2 hover:bg-gray-200 rounded-full p-0.5"
|
||||
on:click|stopPropagation|preventDefault={() => (search = '')}
|
||||
>
|
||||
<X size="14" />
|
||||
</button>
|
||||
{/if}
|
||||
<div class="overflow-auto h-full min-w-[150px] w-full relative flex flex-col">
|
||||
<div class="sticky z-50 top-0 left-0 w-full bg-white px-2 pb-2">
|
||||
<div class="relative">
|
||||
<input
|
||||
bind:value={search}
|
||||
class="px-2 py-1 border border-gray-300 rounded-sm {search ? 'pr-8' : ''}"
|
||||
placeholder="Search outputs..."
|
||||
/>
|
||||
{#if search}
|
||||
<button
|
||||
class="absolute right-2 top-1/2 transform -translate-y-1/2 hover:bg-gray-200 rounded-full p-0.5"
|
||||
on:click|stopPropagation|preventDefault={() => (search = '')}
|
||||
>
|
||||
<X size="14" />
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{#each filteredPanels as [componentId, outputs] (componentId)}
|
||||
{#if outputs.length > 0 && $worldStore?.outputsById[componentId]}
|
||||
{@const name = getComponentNameById(componentId)}
|
||||
<div>
|
||||
<div
|
||||
class="flex {$connectingInput?.opened
|
||||
? 'bg-white z-50'
|
||||
: ''} flex-row justify-between w-full"
|
||||
>
|
||||
<button
|
||||
on:click|stopPropagation|preventDefault={$connectingInput.opened
|
||||
? undefined
|
||||
: () => ($selectedComponent = componentId)}
|
||||
class={classNames(
|
||||
'px-2 text-2xs py-0.5 border-t border-x font-bold rounded-t-sm w-fit',
|
||||
$selectedComponent === componentId
|
||||
? ' bg-blue-500 text-white border-blue-500'
|
||||
: 'bg-gray-100 text-gray-500 border-gray-200'
|
||||
)}
|
||||
>
|
||||
{componentId}
|
||||
</button>
|
||||
<span
|
||||
class={classNames(
|
||||
'px-1 text-2xs py-0.5 font-semibold rounded-t-sm w-fit',
|
||||
'bg-gray-700 text-white'
|
||||
)}
|
||||
>
|
||||
{name}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
class={classNames(
|
||||
$connectingInput?.opened ? 'bg-white z-50' : '',
|
||||
`w-full py-2 grow border relative break-all `,
|
||||
$selectedComponent === componentId ? 'border border-blue-500 ' : '',
|
||||
$connectingInput.hoveredComponent === componentId ? 'outline outline-blue-500' : ''
|
||||
)}
|
||||
>
|
||||
{#key $selectedComponent}
|
||||
{#key $connectingInput?.opened}
|
||||
<ComponentOutputViewer
|
||||
outputs={$connectingInput?.opened && $selectedComponent === componentId
|
||||
? name == 'Table'
|
||||
? ['search']
|
||||
: []
|
||||
: outputs}
|
||||
<div id="app-tutorial-2" class="relative p-2">
|
||||
{#each filteredPanels as [componentId, outputs] (componentId)}
|
||||
<div
|
||||
animate:flip={{ duration: 300 }}
|
||||
in:fade|local={{ duration: 100, delay: 50 }}
|
||||
out:fade|local={{ duration: 100 }}
|
||||
class="pb-2"
|
||||
>
|
||||
{#if outputs.length > 0 && $worldStore?.outputsById[componentId]}
|
||||
{@const name = getComponentNameById(componentId)}
|
||||
<div>
|
||||
<div
|
||||
class="flex {$connectingInput?.opened
|
||||
? 'bg-white z-50'
|
||||
: ''} flex-row justify-between w-full"
|
||||
>
|
||||
<button
|
||||
on:click|stopPropagation|preventDefault={$connectingInput.opened
|
||||
? undefined
|
||||
: () => ($selectedComponent = componentId)}
|
||||
class={classNames(
|
||||
'px-2 text-2xs py-0.5 border-t border-x font-bold rounded-t-sm w-fit',
|
||||
$selectedComponent === componentId
|
||||
? ' bg-indigo-500/90 text-white border-indigo-500/90'
|
||||
: 'bg-gray-100 text-gray-500 border-gray-200'
|
||||
)}
|
||||
>
|
||||
{componentId}
|
||||
on:select={({ detail }) => {
|
||||
connectInput(componentId, detail)
|
||||
}}
|
||||
/>
|
||||
{/key}
|
||||
{/key}
|
||||
</div>
|
||||
</button>
|
||||
<span
|
||||
class={classNames(
|
||||
'px-1 text-2xs py-0.5 font-semibold rounded-t-sm w-fit',
|
||||
'bg-gray-700 text-white'
|
||||
)}
|
||||
>
|
||||
{name}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class={classNames(
|
||||
$connectingInput?.opened ? 'bg-white z-50' : '',
|
||||
`w-full py-2 grow border relative break-all `,
|
||||
$selectedComponent === componentId ? 'border border-indigo-500/90 ' : '',
|
||||
$connectingInput.hoveredComponent === componentId
|
||||
? 'outline outline-indigo-500/90'
|
||||
: ''
|
||||
)}
|
||||
>
|
||||
{#key $selectedComponent}
|
||||
{#key $connectingInput?.opened}
|
||||
<ComponentOutputViewer
|
||||
outputs={$connectingInput?.opened && $selectedComponent === componentId
|
||||
? name == 'Table'
|
||||
? ['search']
|
||||
: []
|
||||
: outputs}
|
||||
{componentId}
|
||||
on:select={({ detail }) => {
|
||||
connectInput(componentId, detail)
|
||||
}}
|
||||
/>
|
||||
{/key}
|
||||
{/key}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
{:else}
|
||||
<div
|
||||
in:fade|local={{ duration: 50, delay: 100 }}
|
||||
out:fade|local={{ duration: 50 }}
|
||||
class="absolute left-0 top-0 w-full text-sm text-gray-500 text-center py-4 px-2"
|
||||
>
|
||||
No outputs found
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</PanelSection>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
import { Building, Globe2 } from 'lucide-svelte'
|
||||
import { createEventDispatcher, getContext } from 'svelte'
|
||||
import { fly } from 'svelte/transition'
|
||||
import type { AppEditorContext } from '../../types'
|
||||
import type { AppViewerContext } from '../../types'
|
||||
import { defaultCode } from '../component'
|
||||
import InlineScriptList from '../settingsPanel/mainInput/InlineScriptList.svelte'
|
||||
import WorkspaceScriptList from '../settingsPanel/mainInput/WorkspaceScriptList.svelte'
|
||||
@@ -23,7 +23,7 @@
|
||||
let filter: string = ''
|
||||
let picker: Drawer
|
||||
|
||||
const { appPath, app } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { appPath, app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
async function inferInlineScriptSchema(
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import Button from '$lib/components/common/button/Button.svelte'
|
||||
import type { Preview } from '$lib/gen'
|
||||
import { createEventDispatcher, getContext, onMount } from 'svelte'
|
||||
import type { AppEditorContext, InlineScript } from '../../types'
|
||||
import type { AppViewerContext, InlineScript } from '../../types'
|
||||
import { CornerDownLeft, Maximize2, Plus, Trash2, X } from 'lucide-svelte'
|
||||
import InlineScriptEditorDrawer from './InlineScriptEditorDrawer.svelte'
|
||||
import { inferArgs } from '$lib/infer'
|
||||
@@ -26,7 +26,7 @@
|
||||
export let fields: Record<string, AppInput> = {}
|
||||
export let syncFields: boolean = false
|
||||
|
||||
const { runnableComponents, stateId } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { runnableComponents, stateId } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let editor: Editor
|
||||
let validCode = true
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppEditorContext } from '../../types'
|
||||
import type { AppViewerContext } from '../../types'
|
||||
import SplitPanesWrapper from '$lib/components/splitPanes/SplitPanesWrapper.svelte'
|
||||
import { Pane, Splitpanes } from 'svelte-splitpanes'
|
||||
import InlineScriptsPanelList from './InlineScriptsPanelList.svelte'
|
||||
@@ -9,7 +9,7 @@
|
||||
import InlineScriptEditorPanel from './InlineScriptEditorPanel.svelte'
|
||||
|
||||
const { app, staticOutputs, runnableComponents } =
|
||||
getContext<AppEditorContext>('AppEditorContext')
|
||||
getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let selectedScriptComponentId: string | undefined = undefined
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script lang="ts">
|
||||
import { Badge, Button } from '$lib/components/common'
|
||||
import { faPlus } from '@fortawesome/free-solid-svg-icons'
|
||||
import { Plus } from 'lucide-svelte'
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppEditorContext } from '../../types'
|
||||
import Tooltip from '../../../Tooltip.svelte'
|
||||
import type { AppViewerContext } from '../../types'
|
||||
import { getAllScriptNames } from '../../utils'
|
||||
import PanelSection from '../settingsPanel/common/PanelSection.svelte'
|
||||
import { getAppScripts } from './utils'
|
||||
@@ -11,8 +11,7 @@
|
||||
const PREFIX = 'script-selector-' as const
|
||||
|
||||
export let selectedScriptComponentId: string | undefined = undefined
|
||||
const { app, selectedComponent } = getContext<AppEditorContext>('AppEditorContext')
|
||||
let list: HTMLElement
|
||||
const { app, selectedComponent } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
function selectScript(id: string) {
|
||||
selectedScriptComponentId = id
|
||||
@@ -53,106 +52,110 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="sticky top-0 py-0.5 px-2 text-left bg-gray-50 text-gray-800 text-xs font-semibold tracking-wide border-b border-gray-300"
|
||||
>
|
||||
Runnables
|
||||
</div>
|
||||
<div bind:this={list} class="grow flex flex-col gap-4">
|
||||
<PanelSection title="Inline scripts" smallPadding>
|
||||
<div class="flex flex-col gap-2 w-full">
|
||||
{#if runnables.inline.length > 0}
|
||||
<div class="flex gap-1 flex-col ">
|
||||
{#each runnables.inline as { name, id }, index (index)}
|
||||
<PanelSection title="Runnables" smallPadding>
|
||||
<div class="w-full flex flex-col gap-6 py-1">
|
||||
<div>
|
||||
<div class="text-sm font-semibold truncate mb-1"> Inline scripts </div>
|
||||
<div class="flex flex-col gap-2 w-full">
|
||||
{#if runnables.inline.length > 0}
|
||||
<div class="flex gap-1 flex-col ">
|
||||
{#each runnables.inline as { name, id }, index (index)}
|
||||
<button
|
||||
id={PREFIX + id}
|
||||
class="border flex gap-1 truncate font-normal justify-between w-full items-center py-1 px-2 rounded-sm duration-200
|
||||
{selectedScriptComponentId === id ? 'border-blue-500 bg-blue-100' : 'hover:bg-blue-50'}"
|
||||
on:click={() => selectScript(id)}
|
||||
>
|
||||
<span class="text-2xs truncate">{name}</span>
|
||||
<div>
|
||||
<Badge color="dark-indigo">{id}</Badge>
|
||||
</div>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{#if $app.unusedInlineScripts?.length > 0}
|
||||
<div class="flex gap-1 flex-col ">
|
||||
{#each $app.unusedInlineScripts as unusedInlineScript, index (index)}
|
||||
{@const id = `unused-${index}`}
|
||||
<button
|
||||
id={PREFIX + id}
|
||||
class="border flex gap-1 truncate font-normal justify-between w-full items-center py-1 px-2 rounded-sm duration-200
|
||||
{selectedScriptComponentId === id ? 'border-blue-500 bg-blue-100' : 'hover:bg-blue-50'}"
|
||||
on:click={() => selectScript(id)}
|
||||
>
|
||||
<span class="text-2xs truncate">{unusedInlineScript.name}</span>
|
||||
<Badge color="red">Detached</Badge>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{#if runnables.inline.length == 0 && $app.unusedInlineScripts?.length == 0}
|
||||
<div class="text-xs text-gray-500">No inline scripts</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="text-sm font-semibold truncate mb-1"> Workspace/Hub </div>
|
||||
<div class="flex flex-col gap-1 w-full">
|
||||
{#if runnables.imported.length > 0}
|
||||
{#each runnables.imported as { name, id }, index (index)}
|
||||
<button
|
||||
id={PREFIX + id}
|
||||
class="border flex gap-1 truncate font-normal justify-between w-full items-center py-1 px-2 rounded-sm duration-200
|
||||
{selectedScriptComponentId === id ? 'border-blue-500 bg-blue-100' : 'hover:bg-blue-50'}"
|
||||
class="border flex gap-1 truncate font-normal justify-between w-full items-center py-1 px-2 rounded-sm duration-200
|
||||
{selectedScriptComponentId === id ? 'border-blue-500 bg-blue-100' : 'hover:bg-blue-50'}"
|
||||
on:click={() => selectScript(id)}
|
||||
>
|
||||
<span class="text-2xs truncate">{name}</span>
|
||||
<div>
|
||||
<Badge color="dark-indigo">{id}</Badge>
|
||||
</div>
|
||||
<Badge color="dark-indigo">{id}</Badge>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="text-xs text-gray-500">No imported scripts/flows</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if $app.unusedInlineScripts?.length > 0}
|
||||
<div class="flex gap-1 flex-col ">
|
||||
{#each $app.unusedInlineScripts as unusedInlineScript, index (index)}
|
||||
{@const id = `unused-${index}`}
|
||||
<div>
|
||||
<div class="w-full flex justify-between items-center">
|
||||
<div class="text-sm font-semibold truncate mb-1">
|
||||
Background scripts
|
||||
<Tooltip class="mb-0.5">
|
||||
Background scripts are triggered upon global refresh or when their input changes. The
|
||||
result of a background script can be shared among many components.
|
||||
</Tooltip>
|
||||
</div>
|
||||
<Button
|
||||
size="xs"
|
||||
color="light"
|
||||
variant="border"
|
||||
btnClasses="!rounded-full !p-1"
|
||||
title="Create a new background script"
|
||||
aria-label="Create a new background script"
|
||||
on:click={createBackgroundScript}
|
||||
>
|
||||
<Plus size={14} class="text-gray-500" />
|
||||
</Button>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1 w-full">
|
||||
{#if $app.hiddenInlineScripts?.length > 0}
|
||||
{#each $app.hiddenInlineScripts as { name }, index (index)}
|
||||
{@const id = `bg_${index}`}
|
||||
<button
|
||||
id={PREFIX + id}
|
||||
class="border flex gap-1 truncate font-normal justify-between w-full items-center py-1 px-2 rounded-sm duration-200
|
||||
{selectedScriptComponentId === id ? 'border-blue-500 bg-blue-100' : 'hover:bg-blue-50'}"
|
||||
class="border flex gap-1 truncate font-normal justify-between w-full items-center py-1 px-2 rounded-sm duration-200
|
||||
{selectedScriptComponentId === id ? 'border-blue-500 bg-blue-100' : 'hover:bg-blue-50'}"
|
||||
on:click={() => selectScript(id)}
|
||||
>
|
||||
<span class="text-2xs truncate">{unusedInlineScript.name}</span>
|
||||
<Badge color="red">Detached</Badge>
|
||||
<span class="text-2xs truncate">{name}</span>
|
||||
<Badge color="dark-indigo">{id}</Badge>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if runnables.inline.length == 0 && $app.unusedInlineScripts?.length == 0}
|
||||
<div class="text-sm text-gray-500">No inline scripts</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="text-xs text-gray-500">No background scripts</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</PanelSection>
|
||||
|
||||
<PanelSection title="Workspace/Hub" smallPadding>
|
||||
<div class="flex flex-col gap-1 w-full">
|
||||
{#if runnables.imported.length > 0}
|
||||
{#each runnables.imported as { name, id }, index (index)}
|
||||
<button
|
||||
id={PREFIX + id}
|
||||
class="border flex gap-1 truncate font-normal justify-between w-full items-center py-1 px-2 rounded-sm duration-200
|
||||
{selectedScriptComponentId === id ? 'border-blue-500 bg-blue-100' : 'hover:bg-blue-50'}"
|
||||
on:click={() => selectScript(id)}
|
||||
>
|
||||
<span class="text-2xs truncate">{name}</span>
|
||||
<Badge color="dark-indigo">{id}</Badge>
|
||||
</button>
|
||||
{/each}
|
||||
{:else}
|
||||
<div class="text-sm text-gray-500">No imported scripts/flows</div>
|
||||
{/if}
|
||||
</div>
|
||||
</PanelSection>
|
||||
|
||||
<PanelSection
|
||||
title="Background scripts"
|
||||
tooltip="Background scripts are triggered upon global refresh or when their input changes. The result
|
||||
of a background script can be shared among many components."
|
||||
smallPadding
|
||||
>
|
||||
<svelte:fragment slot="action">
|
||||
<button
|
||||
class="rounded-full bg-gray-100 hover:bg-gray-200 p-1 border border-gray-200"
|
||||
on:click={createBackgroundScript}
|
||||
>
|
||||
<Plus size={14} class="text-gray-500" />
|
||||
</button>
|
||||
</svelte:fragment>
|
||||
<div class="flex flex-col gap-1 w-full">
|
||||
{#if $app.hiddenInlineScripts?.length > 0}
|
||||
{#each $app.hiddenInlineScripts as { name }, index (index)}
|
||||
{@const id = `bg_${index}`}
|
||||
<button
|
||||
id={PREFIX + id}
|
||||
class="border flex gap-1 truncate font-normal justify-between w-full items-center py-1 px-2 rounded-sm duration-200
|
||||
{selectedScriptComponentId === id ? 'border-blue-500 bg-blue-100' : 'hover:bg-blue-50'}"
|
||||
on:click={() => selectScript(id)}
|
||||
>
|
||||
<span class="text-2xs truncate">{name}</span>
|
||||
<Badge color="dark-indigo">{id}</Badge>
|
||||
</button>
|
||||
{/each}
|
||||
{:else}
|
||||
<div class="text-sm text-gray-500">No items</div>
|
||||
{/if}
|
||||
</div>
|
||||
</PanelSection>
|
||||
</div>
|
||||
</div>
|
||||
</PanelSection>
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
import { faArrowRight, faCode, faPen } from '@fortawesome/free-solid-svg-icons'
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { AppEditorContext } from '../../types'
|
||||
import type { AppViewerContext } from '../../types'
|
||||
|
||||
export let componentInput: AppInput
|
||||
export let disableStatic: boolean = false
|
||||
|
||||
const { onchange } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { onchange } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
$: if (componentInput.fieldType == 'template' && componentInput.type == 'static') {
|
||||
//@ts-ignore
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import Button from '$lib/components/common/button/Button.svelte'
|
||||
import { faChevronDown, faChevronUp, faCopy, faTrashAlt } from '@fortawesome/free-solid-svg-icons'
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppEditorContext } from '../../types'
|
||||
import type { AppEditorContext, AppViewerContext } from '../../types'
|
||||
import PanelSection from './common/PanelSection.svelte'
|
||||
import InputsSpecsEditor from './InputsSpecsEditor.svelte'
|
||||
import TableActions from './TableActions.svelte'
|
||||
@@ -42,9 +42,10 @@
|
||||
selectedComponent,
|
||||
worldStore,
|
||||
focusedGrid,
|
||||
stateId,
|
||||
history
|
||||
} = getContext<AppEditorContext>('AppEditorContext')
|
||||
stateId
|
||||
} = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
const { history } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
function duplicateElement(id: string) {
|
||||
push(history, $app)
|
||||
@@ -80,8 +81,20 @@
|
||||
component?.componentInput?.type === 'template' && $worldStore
|
||||
? buildExtraLib($worldStore?.outputsById ?? {}, component?.id, false)
|
||||
: undefined
|
||||
|
||||
function keydown(event: KeyboardEvent) {
|
||||
switch (event.key) {
|
||||
case 'Delete': {
|
||||
removeGridElement()
|
||||
event.stopPropagation()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={keydown} />
|
||||
|
||||
{#if component}
|
||||
<div class="flex flex-col min-w-[150px] w-full divide-y">
|
||||
{#if component.componentInput}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
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 type { AppViewerContext } from '../../types'
|
||||
import { deleteGridItem } from '../appUtils'
|
||||
import type { AppComponent } from '../component'
|
||||
import PanelSection from './common/PanelSection.svelte'
|
||||
@@ -11,7 +11,7 @@
|
||||
export let component: AppComponent
|
||||
|
||||
const { app, staticOutputs, runnableComponents } =
|
||||
getContext<AppEditorContext>('AppEditorContext')
|
||||
getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
function addTab() {
|
||||
const numberOfPanes = panes.length
|
||||
@@ -53,9 +53,9 @@
|
||||
{#if panes.length == 0}
|
||||
<span class="text-xs text-gray-500">No panes</span>
|
||||
{/if}
|
||||
<div class="flex gap-2 flex-col mt-2">
|
||||
<div class="w-full flex gap-2 flex-col mt-2">
|
||||
{#each panes as value, index (index)}
|
||||
<div class="flex flex-row gap-2 items-center relative">
|
||||
<div class="w-full flex flex-row gap-2 items-center relative">
|
||||
<input type="number" bind:value />
|
||||
|
||||
<div class="absolute top-1 right-1">
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
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 type { AppViewerContext } from '../../types'
|
||||
import { deleteGridItem } from '../appUtils'
|
||||
import type { AppComponent } from '../component'
|
||||
import PanelSection from './common/PanelSection.svelte'
|
||||
@@ -10,8 +10,8 @@
|
||||
export let tabs: string[]
|
||||
export let component: AppComponent
|
||||
|
||||
const { app, staticOutputs, runnableComponents } =
|
||||
getContext<AppEditorContext>('AppEditorContext')
|
||||
const { app, staticOutputs, runnableComponents, focusedGrid } =
|
||||
getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
function addTab() {
|
||||
const numberOfTabs = tabs.length
|
||||
@@ -51,9 +51,9 @@
|
||||
{#if tabs.length == 0}
|
||||
<span class="text-xs text-gray-500">No Tabs</span>
|
||||
{/if}
|
||||
<div class="flex gap-2 flex-col mt-2">
|
||||
<div class="w-full flex gap-2 flex-col mt-2">
|
||||
{#each tabs as value, index (index)}
|
||||
<div class="flex flex-row gap-2 items-center relative">
|
||||
<div class="w-full flex flex-row gap-2 items-center relative">
|
||||
<input type="text" bind:value />
|
||||
|
||||
<div class="absolute top-1 right-1">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import VariableEditor from '$lib/components/VariableEditor.svelte'
|
||||
import type {
|
||||
ConnectedAppInput,
|
||||
EvalAppInput,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user