Compare commits

..

93 Commits

Author SHA1 Message Date
Ruben Fiszel
f4fe71e074 chore(main): release 1.72.0 (#1250)
* chore(main): release 1.72.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2023-03-03 11:12:18 +01:00
Ruben Fiszel
fd4e18f62f fix minSize for app splitpanes to 20 2023-03-03 01:08:20 +01:00
Ruben Fiszel
e428662481 feat(frontend): add creatable select 2023-03-03 01:06:36 +01:00
Ruben Fiszel
b796aeef7a calculate all previous ids inside flows 2023-03-02 20:54:44 +01:00
Ruben Fiszel
55eb48c553 fix(frontend): background script not showing inputs 2023-03-02 17:54:05 +01:00
Ruben Fiszel
a43139fe53 flow improvements 2023-03-02 17:37:51 +01:00
Ruben Fiszel
c4463bb029 fix(backend): improve result retrieval 2023-03-02 16:33:24 +01:00
Ruben Fiszel
cc6eaaf473 fix tailwind JIT for devmode + graph fixes 2023-03-02 14:49:36 +01:00
Ádám Kovács
ed25d9f186 feat(frontend): Add app map component (#1251)
* feat(frontend): Add app map component (wip)

* fix(frontend): Revert

* feat(frontend): sync map configuration (#1252)

* fix(frontend): Map markers

* fix(frontend): Switching between input types

* fix(frontend): Customize map controls

* feat(frontend): Fix output + add set region button

* feat(frontend): Fix output + add set region button

* feat(frontend): Fix output + add set region button

* feat(frontend): Only display set region button on edit mode

---------

Co-authored-by: Faton Ramadani <faton.ramadani14@gmail.com>
2023-03-02 14:25:30 +01:00
Ruben Fiszel
35ea2b27b1 fix(cli): fix workspace option + run script/flow + whoami 2023-03-02 13:21:50 +01:00
Ruben Fiszel
2c1e3b3372 UX nits 2023-03-02 12:08:20 +01:00
Ruben Fiszel
4101d587de remove slide causing issues 2023-03-02 11:59:10 +01:00
Ruben Fiszel
e6ff3ab6cc remove slide causing issues 2023-03-02 11:49:57 +01:00
Ruben Fiszel
8fc6c39129 remove bg-gray-50 from viewed apps 2023-03-02 11:06:11 +01:00
Ruben Fiszel
fcb5cf4d41 revert caddyfileremote target change 2023-03-02 10:39:01 +01:00
Ruben Fiszel
2679386bf8 fix(frontend): fix table bindings 2023-03-02 09:54:30 +01:00
Ryan Rich
580388ce19 Add support for binding server listener to a specific IP address (#1253) 2023-03-02 08:01:50 +01:00
Ruben Fiszel
4e6e66d7b1 fix splitpanes 2023-03-02 02:31:39 +01:00
Faton Ramadani
f4d79ee263 feat(frontend): app splitpanes (#1248)
* feat(frontend): app splitpanes

* feat(frontend): app splitpanes vertical

* feat(frontend): support both splitpanes

* done

* done

* default select value

* container height

---------

Co-authored-by: Ruben Fiszel <ruben@rubenfiszel.com>
2023-03-02 01:30:07 +01:00
Ruben Fiszel
38fb3450c8 fix apps tabs + make inputvalue more resilient 2023-03-01 22:15:25 +01:00
Ruben Fiszel
94b20d2f5e fix(frontend): rework app reactivity 2023-03-01 21:33:23 +01:00
Ruben Fiszel
1753cb7da6 fix(frontend): rework app reactivity 2023-03-01 20:01:59 +01:00
Ruben Fiszel
2a75cd250e fix(backend): incorrect get_result_by_id for list_result job 2023-03-01 12:43:00 +01:00
Ruben Fiszel
29f3fe2663 update sqlx-data.json 2023-03-01 12:01:22 +01:00
Ruben Fiszel
4c913dc4b6 feat(backend): get_result_by_id do a downward pass to find node at any depth (#1249)
* downwardRec

* downwardRec

* any node

* any node

* any node
2023-03-01 11:33:48 +01:00
Ruben Fiszel
5c40ff4290 Update LICENSE 2023-03-01 09:53:26 +01:00
Ruben Fiszel
2bbe112444 handle more undefined cases in app 2023-03-01 08:46:20 +01:00
Ruben Fiszel
90a12f6131 drawer focus 2023-03-01 01:21:32 +01:00
Ruben Fiszel
f3f95fa865 active grid border-dashed for apps 2023-03-01 01:18:10 +01:00
Ruben Fiszel
26784464a4 revert pips change 2023-02-28 22:53:44 +01:00
Ruben Fiszel
c96e2351d9 chore(main): release 1.71.0 (#1242)
* chore(main): release 1.71.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2023-02-28 22:51:57 +01:00
Ruben Fiszel
ddb4916a2e fix app fields 2023-02-28 22:41:21 +01:00
Ádám Kovács
1bb5ed9ae0 fix(frontend): Add more app custom css (#1247)
* fix(frontend): Add number input custom css

* fix(frontend): Add currency input custom css

* fix(frontend): Add slider custom css

* fix(frontend): Add range custom css

* fix(frontend): Add password input custom css

* fix(frontend): Add date input custom css

* fix(frontend): Add tabs custom css

* fix(frontend): Minor stylings

* fix(frontend): Add icon custom css

* fix(frontend): Add dividers custom css

* fix(frontend): Add file input custom css

* fix(frontend): Add image custom css
2023-02-28 21:05:58 +01:00
Ruben Fiszel
b5b32f00b3 fix overflow-y on debug runs 2023-02-28 19:54:20 +01:00
Ruben Fiszel
c06311faf8 add workspace_add events 2023-02-28 19:41:01 +01:00
Ruben Fiszel
8a639b6e7d select input fix 2023-02-28 19:28:36 +01:00
Ruben Fiszel
05f568fb8c display startup info in all cases 2023-02-28 17:52:47 +01:00
Ruben Fiszel
e515c70e71 fix incorrect user sttings redirect 2023-02-28 16:57:00 +01:00
Ruben Fiszel
6adc875610 feat(frontend): drawer for editing workspace scripts in flows 2023-02-28 15:51:56 +01:00
Faton Ramadani
8a0d1158c4 feat(frontend): App drawer (#1246)
* feat(frontend): app drawer

* feat(frontend): app drawer

* feat(frontend): app drawer

* feat(frontend): app drawer wip

* feat(frontend): drawer wip

* feat(frontend): drawer wip

* feat(frontend): app missing prop

* feat(frontend): revert drawer changes

* feat(frontend): highlight subgrid
2023-02-28 15:49:57 +01:00
Ruben Fiszel
ea2ebfa92e fix compile issue 2023-02-28 11:18:09 +01:00
dependabot[bot]
ba856be10d chore(deps): bump docker/metadata-action from 3 to 4 (#1243)
Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 3 to 4.
- [Release notes](https://github.com/docker/metadata-action/releases)
- [Upgrade guide](https://github.com/docker/metadata-action/blob/master/UPGRADE.md)
- [Commits](https://github.com/docker/metadata-action/compare/v3...v4)

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

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

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

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

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

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

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

* fix(frontend): Add form custom css

* fix(frontend): Add form button custom css

* feat(frontend): Add css quick reset button

* feat(frontend): Filter component css by usage

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

* fix(frontend): Add pie chart custom css

* fix(frontend): Add bar chart custom css

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

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

* fix(frontend): Add scatter chart custom css

* fix(frontend): Add table custom css

* fix(frontend): Revert container custom styles

* fix(frontend): Add toggle custom css

* fix(frontend): Add text input custom css

* fix(frontend): Update

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

* Apply automatic changes

---------

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

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

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

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2023-02-27 09:25:03 +01:00
Ruben Fiszel
04a076f1db fix(cli): bump cli to non broken client 1.69.3 2023-02-27 09:24:18 +01:00
Ruben Fiszel
ebd2e0323e fix stripe checkout 2023-02-27 08:49:36 +01:00
Ruben Fiszel
cd25570003 fix minor editor bugs 2023-02-27 00:53:49 +01:00
Ruben Fiszel
e95f8ef6bf add none color for buttons 2023-02-27 00:27:20 +01:00
Ruben Fiszel
c3d1c8ac39 fix many apps bugs 2023-02-26 23:43:00 +01:00
Ruben Fiszel
d38aff2fe2 fix: app setup and sync now uses 1.69.3 2023-02-26 14:09:16 +01:00
Ruben Fiszel
95851ea486 minor app nits 2023-02-26 13:58:51 +01:00
Ruben Fiszel
b690d801d4 feat(apps): add ag grid 2023-02-25 13:11:50 +01:00
Faton Ramadani
104e4ac5e7 feat(frontend): move to other grid (#1230)
* feat(frontend): move to other grid

* feat(frontend): wip

* feat(frontend): Fix component move

* fix(frontend): Fix build

* feat(frontend): Fix moving

* fix(frontend): Simplify code
2023-02-25 11:31:12 +01:00
Faton Ramadani
e87f4fc44b fix(frontend): Fix duplication (#1237) 2023-02-25 10:18:21 +01:00
Ruben Fiszel
e1f686d850 fix(frontend): fix graph viewer id assignment 2023-02-25 08:48:24 +01:00
Ruben Fiszel
7da7dac3ac chore(main): release 1.69.3 (#1234) 2023-02-24 21:11:58 +01:00
Ruben Fiszel
c2e5afd4e0 fix(deno): fix denoify buffer handling 2023-02-24 21:09:37 +01:00
Ruben Fiszel
ad9c386f41 making slider/range values connectable 2023-02-24 20:50:01 +01:00
Ruben Fiszel
a4e3f98b7d chore(main): release 1.69.2 (#1233)
* chore(main): release 1.69.2

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2023-02-24 19:57:50 +01:00
Ruben Fiszel
dd28308c3c fix(app): fix all nested behavior 2023-02-24 19:52:20 +01:00
Ruben Fiszel
833c2655ea push 2023-02-24 19:52:20 +01:00
Faton Ramadani
a8295d0b5a fix(frontend): Fix findGridItem 2023-02-24 19:52:20 +01:00
Ruben Fiszel
897e2f6b53 flatten 2023-02-24 19:52:20 +01:00
Faton Ramadani
5bb77edf45 fix(frontend): Fix findGridItem 2023-02-24 19:52:20 +01:00
Faton Ramadani
8ddcf4d9c1 fix(frontend): Fix next id 2023-02-24 19:52:20 +01:00
Faton Ramadani
33ebe2da8e fix(frontend): wip 2023-02-24 19:52:20 +01:00
Ruben Fiszel
b3ee747014 pushed 2023-02-24 19:52:20 +01:00
Faton Ramadani
fa105b4cae fix(frontend): gridtab 2023-02-24 19:52:20 +01:00
Faton Ramadani
483407cdf0 fix(frontend): duplicate 2023-02-24 19:52:20 +01:00
Faton Ramadani
008c30fcaa fix(frontend): delete grid item 2023-02-24 19:52:20 +01:00
Ruben Fiszel
3387bb0d83 script editor panel 2023-02-24 19:52:20 +01:00
Ruben Fiszel
e08e7e4ae6 settings panel 2023-02-24 19:52:20 +01:00
Faton Ramadani
ea1b2c29b9 fix(frontend): rewrote utils 2023-02-24 19:52:20 +01:00
Ruben Fiszel
4ad6fbefd3 flatten 2023-02-24 19:52:20 +01:00
Ruben Fiszel
397ecd64d4 chore(main): release 1.69.1 (#1231)
* chore(main): release 1.69.1

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2023-02-24 17:26:53 +01:00
177 changed files with 5718 additions and 1460 deletions

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,112 @@
# Changelog
## [1.72.0](https://github.com/windmill-labs/windmill/compare/v1.71.0...v1.72.0) (2023-03-02)
### Features
* **backend:** get_result_by_id do a downward pass to find node at any depth ([#1249](https://github.com/windmill-labs/windmill/issues/1249)) ([4c913dc](https://github.com/windmill-labs/windmill/commit/4c913dc4b6be03571a015c97a13829adffb61479))
* **frontend:** Add app map component ([#1251](https://github.com/windmill-labs/windmill/issues/1251)) ([ed25d9f](https://github.com/windmill-labs/windmill/commit/ed25d9f186d9925f75404cb193a025d8a41c4540))
* **frontend:** app splitpanes ([#1248](https://github.com/windmill-labs/windmill/issues/1248)) ([f4d79ee](https://github.com/windmill-labs/windmill/commit/f4d79ee2633e6cdab0fa2410108b31cfa77e10da))
### Bug Fixes
* **backend:** improve result retrieval ([c4463bb](https://github.com/windmill-labs/windmill/commit/c4463bb029907f3c8d77abb194f872aae7876bf6))
* **backend:** incorrect get_result_by_id for list_result job ([2a75cd2](https://github.com/windmill-labs/windmill/commit/2a75cd250ea5e01849fc8bbb69bf44f147d0acb8))
* **cli:** fix workspace option + run script/flow + whoami ([35ea2b2](https://github.com/windmill-labs/windmill/commit/35ea2b27b12159c68c8507ec1f8686028c975387))
* **frontend:** background script not showing inputs ([55eb48c](https://github.com/windmill-labs/windmill/commit/55eb48c55332431304cedbf3bcbbbcff61ec3645))
* **frontend:** fix table bindings ([2679386](https://github.com/windmill-labs/windmill/commit/2679386bf87a56352269911bd89e52df5ee9f314))
* **frontend:** rework app reactivity ([94b20d2](https://github.com/windmill-labs/windmill/commit/94b20d2f5e3b551974c57ea82b6e3dc16e97b9b8))
* **frontend:** rework app reactivity ([1753cb7](https://github.com/windmill-labs/windmill/commit/1753cb7da658f47be974c15da82c71a8e19309a6))
## [1.71.0](https://github.com/windmill-labs/windmill/compare/v1.70.1...v1.71.0) (2023-02-28)
### Features
* **backend:** use counter for sleep/execution/pull durations ([e568690](https://github.com/windmill-labs/windmill/commit/e56869092a03fec4703ddd9ef65c89edb8122962))
* **cli:** add autocompletions ([287b2db](https://github.com/windmill-labs/windmill/commit/287b2db22f7b56e90bcd0c4727c00096695c2e0d))
* **frontend:** App drawer ([#1246](https://github.com/windmill-labs/windmill/issues/1246)) ([8a0d115](https://github.com/windmill-labs/windmill/commit/8a0d1158c4d7e970cb91e1adf4838e5efdbb39ff))
* **frontend:** drawer for editing workspace scripts in flows ([6adc875](https://github.com/windmill-labs/windmill/commit/6adc87561070d8aceaba1838008cd7e6be2e2660))
### Bug Fixes
* **frontend:** Add more app custom css ([#1229](https://github.com/windmill-labs/windmill/issues/1229)) ([a4e4d18](https://github.com/windmill-labs/windmill/commit/a4e4d188ad10443dd0b7f104389594efc768dc59))
* **frontend:** Add more app custom css ([#1247](https://github.com/windmill-labs/windmill/issues/1247)) ([1bb5ed9](https://github.com/windmill-labs/windmill/commit/1bb5ed9ae01fd7998b06833b6222e5dd5d774d35))
* **frontend:** display currently selected filter even if not in list ([42d1cd6](https://github.com/windmill-labs/windmill/commit/42d1cd6456620ba917c560c87d736dc93634adff))
* **frontend:** Fix deeply nested move ([#1245](https://github.com/windmill-labs/windmill/issues/1245)) ([a67f10e](https://github.com/windmill-labs/windmill/commit/a67f10eeb6fdb44bbb3a510badcc5ad0ae187a2b))
* **frontend:** invisible subgrids have h-0 + app policies fix ([2244e83](https://github.com/windmill-labs/windmill/commit/2244e83b9da803a4cf46ab0825d7cb6cb0e24872))
## [1.70.1](https://github.com/windmill-labs/windmill/compare/v1.70.0...v1.70.1) (2023-02-27)
### Bug Fixes
* **cli:** make cli resilient to systems without openable browsers ([c051ffe](https://github.com/windmill-labs/windmill/commit/c051ffeb42c1cff609f93da7745036ea722e17d4))
* **frontend:** Disable move in nested subgrid ([#1238](https://github.com/windmill-labs/windmill/issues/1238)) ([70eab30](https://github.com/windmill-labs/windmill/commit/70eab303bd45111ae198d9b710bfd6f9f59e53b0))
* **frontend:** Fix inline scripts list ([#1240](https://github.com/windmill-labs/windmill/issues/1240)) ([97602ac](https://github.com/windmill-labs/windmill/commit/97602ac6db1404d36d160a431ffcea6c0f567a48))
* **frontend:** Fix subgrid lock ([#1232](https://github.com/windmill-labs/windmill/issues/1232)) ([8ee9d67](https://github.com/windmill-labs/windmill/commit/8ee9d67f4faa91446338b41c664ef91913eb8b81))
## [1.70.1](https://github.com/windmill-labs/windmill/compare/v1.70.0...v1.70.1) (2023-02-27)
### Bug Fixes
* **cli:** make cli resilient to systems without openable browsers ([c051ffe](https://github.com/windmill-labs/windmill/commit/c051ffeb42c1cff609f93da7745036ea722e17d4))
* **frontend:** Disable move in nested subgrid ([#1238](https://github.com/windmill-labs/windmill/issues/1238)) ([70eab30](https://github.com/windmill-labs/windmill/commit/70eab303bd45111ae198d9b710bfd6f9f59e53b0))
* **frontend:** Fix subgrid lock ([#1232](https://github.com/windmill-labs/windmill/issues/1232)) ([8ee9d67](https://github.com/windmill-labs/windmill/commit/8ee9d67f4faa91446338b41c664ef91913eb8b81))
## [1.70.0](https://github.com/windmill-labs/windmill/compare/v1.69.3...v1.70.0) (2023-02-27)
### Features
* **apps:** add ag grid ([b690d80](https://github.com/windmill-labs/windmill/commit/b690d801d4aa5695ee558e81d1ed114074dfcb83))
* **frontend:** move to other grid ([#1230](https://github.com/windmill-labs/windmill/issues/1230)) ([104e4ac](https://github.com/windmill-labs/windmill/commit/104e4ac5e790c30e6fb6b27726776693038d4f19))
### Bug Fixes
* app setup and sync now uses 1.69.3 ([d38aff2](https://github.com/windmill-labs/windmill/commit/d38aff2fe228f23eb18c3991392928c064e6aca2))
* **frontend:** Fix duplication ([#1237](https://github.com/windmill-labs/windmill/issues/1237)) ([e87f4fc](https://github.com/windmill-labs/windmill/commit/e87f4fc44b847a573f5acafc0348fbcbfcb2258f))
* **frontend:** fix graph viewer id assignment ([e1f686d](https://github.com/windmill-labs/windmill/commit/e1f686d8508cfc1f73c43be08facc44217ca8de0))
## [1.69.3](https://github.com/windmill-labs/windmill/compare/v1.69.2...v1.69.3) (2023-02-24)
### Bug Fixes
* **deno:** fix denoify buffer handling ([c2e5afd](https://github.com/windmill-labs/windmill/commit/c2e5afd4e07fb63375832f308da8c744616ee188))
## [1.69.2](https://github.com/windmill-labs/windmill/compare/v1.69.1...v1.69.2) (2023-02-24)
### Bug Fixes
* **app:** fix all nested behavior ([dd28308](https://github.com/windmill-labs/windmill/commit/dd28308c3cf1877ba3f19dcd2bd20bf1c7896a99))
* **frontend:** delete grid item ([008c30f](https://github.com/windmill-labs/windmill/commit/008c30fcaad64af512407f9889a9881fafac0868))
* **frontend:** duplicate ([483407c](https://github.com/windmill-labs/windmill/commit/483407cdf0e1ed61de180a904934e950fed4adc3))
* **frontend:** Fix findGridItem ([a8295d0](https://github.com/windmill-labs/windmill/commit/a8295d0b5acd08cec42b7939d907df5c25132644))
* **frontend:** Fix findGridItem ([5bb77ed](https://github.com/windmill-labs/windmill/commit/5bb77edf45740a75e969b1bef31580271c9d5505))
* **frontend:** Fix next id ([8ddcf4d](https://github.com/windmill-labs/windmill/commit/8ddcf4d9c1a8d6dd20ee241a3f308811c49e58f1))
* **frontend:** gridtab ([fa105b4](https://github.com/windmill-labs/windmill/commit/fa105b4caeaa2d0e9704a48f6caf8d846839c23e))
* **frontend:** rewrote utils ([ea1b2c2](https://github.com/windmill-labs/windmill/commit/ea1b2c29b95282df347ef9c5973917fa3880e843))
* **frontend:** wip ([33ebe2d](https://github.com/windmill-labs/windmill/commit/33ebe2da8e81476be62a2567d5012573a8a010b6))
## [1.69.1](https://github.com/windmill-labs/windmill/compare/v1.69.0...v1.69.1) (2023-02-24)
### Bug Fixes
* **deno:** remove mysql support waiting for deno fix ([dd7e8c7](https://github.com/windmill-labs/windmill/commit/dd7e8c742c83f6a1d13e4343ca626c0b5efc06fb))
* **deno:** remove mysql support waiting for deno fix ([2f78132](https://github.com/windmill-labs/windmill/commit/2f78132e081bdf3d7468e022f0e981ebfa52cfb3))
* **frontend:** containers and tab fixes v1 ([27cac3f](https://github.com/windmill-labs/windmill/commit/27cac3ffe69c4dac160e9e55ffd1eb8ea348d487))
* **frontend:** containers and tab fixes v1 ([705703a](https://github.com/windmill-labs/windmill/commit/705703a5e2f2dc7ceb4c215221f72bf624799841))
* **frontend:** containers and tab fixes v1 ([fac31c6](https://github.com/windmill-labs/windmill/commit/fac31c6628b289ad6aae92434e312c4be281a4d2))
## [1.69.0](https://github.com/windmill-labs/windmill/compare/v1.68.0...v1.69.0) (2023-02-23)

View File

@@ -8,5 +8,9 @@ or belonging to one of the below cases:
The files under backend/ are AGPL Licensed.
The files under frontend/ are AGPL Licensed.
The files under python-client/ are Apache 2.0 Licensed.
The files under community/ are Apache 2.0 Licensed.
The files under python-client/ deno-client/ go-client/ are Apache 2.0 Licensed.
The openapi files, including the OpenFlow spec is Apache 2.0 Licensed.
All third party components incorporated into the Windmill Software are licensed under the
original license provided by the owner of the applicable component.

View File

@@ -284,6 +284,7 @@ you to have it being synced automatically everyday.
| ------------------------- | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- |
| DATABASE_URL | | The Postgres database url. | All |
| DISABLE_NSJAIL | true | Disable Nsjail Sandboxing | Worker |
| SERVER_BIND_ADDR | 0.0.0.0 | IP Address on which to bind listening socket | Server |
| PORT | 8000 | Exposed port | Server | |
| NUM_WORKERS | 3 | The number of worker per Worker instance (set to 1 on Eks to have 1 pod = 1 worker, set to 0 for an API only instance) | Worker |
| DISABLE_SERVER | false | Binary would operate as a worker only instance | Worker |

237
backend/Cargo.lock generated
View File

@@ -129,12 +129,11 @@ dependencies = [
[[package]]
name = "async-lock"
version = "2.6.0"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8101efe8695a6c17e02911402145357e718ac92d3ff88ae8419e84b1707b685"
checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7"
dependencies = [
"event-listener",
"futures-lite",
]
[[package]]
@@ -255,9 +254,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "axum"
version = "0.6.7"
version = "0.6.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fb79c228270dcf2426e74864cabc94babb5dbab01a4314e702d2f16540e1591"
checksum = "6137c6234afb339e75e764c866e3594900f0211e1315d33779f269bbe2ec6967"
dependencies = [
"async-trait",
"axum-core",
@@ -324,9 +323,9 @@ checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
[[package]]
name = "base64ct"
version = "1.5.3"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf"
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
[[package]]
name = "better_scoped_tls"
@@ -337,15 +336,6 @@ dependencies = [
"scoped-tls",
]
[[package]]
name = "bincode"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
dependencies = [
"serde",
]
[[package]]
name = "bit-set"
version = "0.5.3"
@@ -525,9 +515,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.1.6"
version = "4.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0b0588d44d4d63a87dbd75c136c166bbfd9a86a31cb89e09906521c7d3f5e3"
checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5"
dependencies = [
"bitflags",
"clap_derive",
@@ -540,9 +530,9 @@ dependencies = [
[[package]]
name = "clap_derive"
version = "4.1.0"
version = "4.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8"
checksum = "44bec8e5c9d09e439c4335b1af0abaab56dcf3b94999a936e1bb47b9134288f0"
dependencies = [
"heck",
"proc-macro-error",
@@ -553,9 +543,9 @@ dependencies = [
[[package]]
name = "clap_lex"
version = "0.3.1"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade"
checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09"
dependencies = [
"os_str_bytes",
]
@@ -581,9 +571,9 @@ dependencies = [
[[package]]
name = "const-oid"
version = "0.9.1"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cec318a675afcb6a1ea1d4340e2d377e56e47c266f28043ceccbf4412ddfdd3b"
checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913"
[[package]]
name = "const_format"
@@ -619,7 +609,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7efb37c3e1ccb1ff97164ad95ac1606e8ccd35b3fa0a7d99a304c7f4a428cc24"
dependencies = [
"percent-encoding",
"time 0.3.19",
"time 0.3.20",
"version_check",
]
@@ -704,9 +694,9 @@ dependencies = [
[[package]]
name = "crossbeam-utils"
version = "0.8.14"
version = "0.8.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f"
checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b"
dependencies = [
"cfg-if",
]
@@ -966,9 +956,9 @@ checksum = "03d8c417d7a8cb362e0c37e5d815f5eb7c37f79ff93707329d5a194e42e54ca0"
[[package]]
name = "dyn-clone"
version = "1.0.10"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9b0705efd4599c15a38151f4721f7bc388306f61084d3bfd50bd07fbca5cb60"
checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30"
[[package]]
name = "dyn-iter"
@@ -987,9 +977,9 @@ dependencies = [
[[package]]
name = "ena"
version = "0.14.0"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3"
checksum = "b2e5d13ca2353ab7d0230988629def93914a8c4015f621f9b13ed2955614731d"
dependencies = [
"log",
]
@@ -1324,9 +1314,9 @@ dependencies = [
[[package]]
name = "h2"
version = "0.3.15"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4"
checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d"
dependencies = [
"bytes",
"fnv",
@@ -1678,9 +1668,9 @@ checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
[[package]]
name = "jobserver"
version = "0.1.25"
version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b"
checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2"
dependencies = [
"libc",
]
@@ -2048,15 +2038,6 @@ dependencies = [
"minimal-lexical",
]
[[package]]
name = "nom8"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8"
dependencies = [
"memchr",
]
[[package]]
name = "nu-ansi-term"
version = "0.46.0"
@@ -2103,7 +2084,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d"
dependencies = [
"num-traits",
"serde",
]
[[package]]
@@ -2147,27 +2127,6 @@ dependencies = [
"libc",
]
[[package]]
name = "num_enum"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e0072973714303aa6e3631c7e8e777970cf4bdd25dc4932e41031027b8bcc4e"
dependencies = [
"num_enum_derive",
]
[[package]]
name = "num_enum_derive"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0629cbd6b897944899b1f10496d9c4a7ac5878d45fd61bc22e9e79bfbbc29597"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "once_cell"
version = "1.17.1"
@@ -2516,9 +2475,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
[[package]]
name = "proc-macro-crate"
version = "1.3.0"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66618389e4ec1c7afe67d51a9bf34ff9236480f8d51e7489b7d5ab0303c13f34"
checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
dependencies = [
"once_cell",
"toml_edit",
@@ -2566,7 +2525,7 @@ dependencies = [
[[package]]
name = "progenitor"
version = "0.2.1-dev"
source = "git+https://github.com/oxidecomputer/progenitor#82c979df65476fe4dfc2590970ccdf64113e9e0c"
source = "git+https://github.com/oxidecomputer/progenitor#cbe875ad1a4c650cf3af595c90df6fd7421b47c2"
dependencies = [
"anyhow",
"built",
@@ -2584,7 +2543,7 @@ dependencies = [
[[package]]
name = "progenitor-client"
version = "0.2.1-dev"
source = "git+https://github.com/oxidecomputer/progenitor#82c979df65476fe4dfc2590970ccdf64113e9e0c"
source = "git+https://github.com/oxidecomputer/progenitor#cbe875ad1a4c650cf3af595c90df6fd7421b47c2"
dependencies = [
"bytes",
"futures-core",
@@ -2598,7 +2557,7 @@ dependencies = [
[[package]]
name = "progenitor-impl"
version = "0.2.1-dev"
source = "git+https://github.com/oxidecomputer/progenitor#82c979df65476fe4dfc2590970ccdf64113e9e0c"
source = "git+https://github.com/oxidecomputer/progenitor#cbe875ad1a4c650cf3af595c90df6fd7421b47c2"
dependencies = [
"getopts",
"heck",
@@ -2620,7 +2579,7 @@ dependencies = [
[[package]]
name = "progenitor-macro"
version = "0.2.1-dev"
source = "git+https://github.com/oxidecomputer/progenitor#82c979df65476fe4dfc2590970ccdf64113e9e0c"
source = "git+https://github.com/oxidecomputer/progenitor#cbe875ad1a4c650cf3af595c90df6fd7421b47c2"
dependencies = [
"openapiv3",
"proc-macro2",
@@ -2798,15 +2757,6 @@ dependencies = [
"memchr",
]
[[package]]
name = "remove_dir_all"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [
"winapi",
]
[[package]]
name = "reqwest"
version = "0.11.14"
@@ -3003,7 +2953,7 @@ dependencies = [
[[package]]
name = "rustpython-ast"
version = "0.2.0"
source = "git+https://github.com/RustPython/RustPython#c4b67896662b16b5699a779c0e52aa0ca2587fec"
source = "git+https://github.com/RustPython/RustPython#6e4c2fe7866f705bb427200a36d6c9fb46508de3"
dependencies = [
"num-bigint",
"rustpython-compiler-core",
@@ -3012,24 +2962,20 @@ dependencies = [
[[package]]
name = "rustpython-compiler-core"
version = "0.2.0"
source = "git+https://github.com/RustPython/RustPython#c4b67896662b16b5699a779c0e52aa0ca2587fec"
source = "git+https://github.com/RustPython/RustPython#6e4c2fe7866f705bb427200a36d6c9fb46508de3"
dependencies = [
"bincode",
"bitflags",
"bstr",
"itertools",
"lz4_flex",
"num-bigint",
"num-complex",
"num_enum",
"serde",
"thiserror",
]
[[package]]
name = "rustpython-parser"
version = "0.2.0"
source = "git+https://github.com/RustPython/RustPython#c4b67896662b16b5699a779c0e52aa0ca2587fec"
source = "git+https://github.com/RustPython/RustPython#6e4c2fe7866f705bb427200a36d6c9fb46508de3"
dependencies = [
"ahash",
"anyhow",
@@ -3044,7 +2990,6 @@ dependencies = [
"rustc-hash",
"rustpython-ast",
"rustpython-compiler-core",
"thiserror",
"tiny-keccak",
"unic-emoji-char",
"unic-ucd-ident",
@@ -3083,9 +3028,9 @@ dependencies = [
[[package]]
name = "schemars"
version = "0.8.11"
version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a5fb6c61f29e723026dc8e923d94c694313212abbecbbe5f55a7748eec5b307"
checksum = "02c613288622e5f0c3fdc5dbd4db1c5fbe752746b1d1a56a0630b78fd00de44f"
dependencies = [
"chrono",
"dyn-clone",
@@ -3097,9 +3042,9 @@ dependencies = [
[[package]]
name = "schemars_derive"
version = "0.8.11"
version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f188d036977451159430f3b8dc82ec76364a42b7e289c2b18a9a18f4470058e9"
checksum = "109da1e6b197438deb6db99952990c7f959572794b80ff93707d55a232545e7c"
dependencies = [
"proc-macro2",
"quote",
@@ -3697,9 +3642,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
[[package]]
name = "swc_atoms"
version = "0.4.37"
version = "0.4.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f88175a66f5a7c189e752bda520e148317776ecb22c75adc2c2f24c490834bd0"
checksum = "5a172f2e444ae1378286cd27ff2a5cb26eadfd7a77c98ccb0edde8992857be1e"
dependencies = [
"once_cell",
"rustc-hash",
@@ -3711,9 +3656,9 @@ dependencies = [
[[package]]
name = "swc_common"
version = "0.29.32"
version = "0.29.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fc8e0e8109b26be70c82d9709562fc88cbcc09e03c2458221cf216c0088dea2"
checksum = "a8a75c46065858a37cdda2c1c6fd056986e3b7752d5ec332e91ce312c6a22749"
dependencies = [
"ahash",
"ast_node",
@@ -3738,9 +3683,9 @@ dependencies = [
[[package]]
name = "swc_ecma_ast"
version = "0.96.7"
version = "0.98.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "621c66e27fbb6cbb6434a4e2b25e439e9a2583cc3419a4a83eba51d16ac0cd7b"
checksum = "7b1f6bc913d0f1daf0fa713a64660aed27848e0047ee671ab2206ad602730d1f"
dependencies = [
"bitflags",
"is-macro",
@@ -3755,9 +3700,9 @@ dependencies = [
[[package]]
name = "swc_ecma_parser"
version = "0.124.12"
version = "0.127.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89b3f472b3dfbfd279de364d2b014459a281824b938e243a8739037c445d6b6c"
checksum = "3875db58a1515382c670679062133294e5ec88aff08776f5fb6e4f3c09c0f5f9"
dependencies = [
"either",
"enum_kind",
@@ -3823,9 +3768,9 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.108"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d56e159d99e6c2b93995d171050271edb50ecc5288fbc7cc17de8fdce4e58c14"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
@@ -3840,16 +3785,15 @@ checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
[[package]]
name = "tempfile"
version = "3.3.0"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95"
dependencies = [
"cfg-if",
"fastrand",
"libc",
"redox_syscall",
"remove_dir_all",
"winapi",
"rustix",
"windows-sys 0.42.0",
]
[[package]]
@@ -3926,9 +3870,9 @@ dependencies = [
[[package]]
name = "time"
version = "0.3.19"
version = "0.3.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53250a3b3fed8ff8fd988587d8925d26a83ac3845d9e03b220b37f34c2b8d6c2"
checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890"
dependencies = [
"itoa",
"serde",
@@ -3944,9 +3888,9 @@ checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
[[package]]
name = "time-macros"
version = "0.2.7"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a460aeb8de6dcb0f381e1ee05f1cd56fcf5a5f6eb8187ff3d8f0b11078d38b7c"
checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36"
dependencies = [
"time-core",
]
@@ -3977,9 +3921,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.25.0"
version = "1.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af"
checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64"
dependencies = [
"autocfg",
"bytes",
@@ -3993,7 +3937,7 @@ dependencies = [
"socket2",
"tokio-macros",
"tracing",
"windows-sys 0.42.0",
"windows-sys 0.45.0",
]
[[package]]
@@ -4090,19 +4034,19 @@ dependencies = [
[[package]]
name = "toml_datetime"
version = "0.5.1"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4553f467ac8e3d374bc9a177a26801e5d0f9b211aa1673fb137a403afd1c9cf5"
checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622"
[[package]]
name = "toml_edit"
version = "0.18.1"
version = "0.19.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56c59d8dd7d0dcbc6428bf7aa2f0e823e26e43b3c9aca15bbc9475d23e5fa12b"
checksum = "9a1eb0622d28f4b9c90adc4ea4b2b46b47663fde9ac5fafcb14a1369d5508825"
dependencies = [
"indexmap",
"nom8",
"toml_datetime",
"winnow",
]
[[package]]
@@ -4153,9 +4097,9 @@ dependencies = [
[[package]]
name = "tower-http"
version = "0.3.5"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858"
checksum = "5d1d42a9b3f3ec46ba828e8d376aec14592ea199f70a06a548587ecd1c4ab658"
dependencies = [
"bitflags",
"bytes",
@@ -4300,7 +4244,7 @@ checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
[[package]]
name = "typify"
version = "0.0.11-dev"
source = "git+https://github.com/oxidecomputer/typify#d579a526b3cc2e0c36d17fae8df549a03187f177"
source = "git+https://github.com/oxidecomputer/typify#05d65ea62be9061c1abd0c31d955d0248120d301"
dependencies = [
"typify-impl",
"typify-macro",
@@ -4309,7 +4253,7 @@ dependencies = [
[[package]]
name = "typify-impl"
version = "0.0.11-dev"
source = "git+https://github.com/oxidecomputer/typify#d579a526b3cc2e0c36d17fae8df549a03187f177"
source = "git+https://github.com/oxidecomputer/typify#05d65ea62be9061c1abd0c31d955d0248120d301"
dependencies = [
"heck",
"log",
@@ -4327,7 +4271,7 @@ dependencies = [
[[package]]
name = "typify-macro"
version = "0.0.11-dev"
source = "git+https://github.com/oxidecomputer/typify#d579a526b3cc2e0c36d17fae8df549a03187f177"
source = "git+https://github.com/oxidecomputer/typify#05d65ea62be9061c1abd0c31d955d0248120d301"
dependencies = [
"proc-macro2",
"quote",
@@ -4475,9 +4419,11 @@ checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
[[package]]
name = "unicode_names2"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "029df4cc8238cefc911704ff8fa210853a0f3bce2694d8f51181dd41ee0f3301"
version = "0.6.0"
source = "git+https://github.com/youknowone/unicode_names2.git?tag=v0.6.0+character-alias#4ce16aa85cbcdd9cc830410f1a72ef9a235f2fde"
dependencies = [
"phf",
]
[[package]]
name = "unsafe-libyaml"
@@ -4766,7 +4712,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windmill"
version = "1.69.0"
version = "1.72.0"
dependencies = [
"anyhow",
"axum",
@@ -4793,7 +4739,7 @@ dependencies = [
[[package]]
name = "windmill-api"
version = "1.69.0"
version = "1.72.0"
dependencies = [
"anyhow",
"argon2",
@@ -4826,7 +4772,7 @@ dependencies = [
"sql-builder",
"sqlx",
"tempfile",
"time 0.3.19",
"time 0.3.20",
"tokio",
"tokio-tar",
"tokio-util",
@@ -4848,7 +4794,7 @@ dependencies = [
[[package]]
name = "windmill-api-client"
version = "1.69.0"
version = "1.72.0"
dependencies = [
"base64 0.21.0",
"chrono",
@@ -4863,7 +4809,7 @@ dependencies = [
[[package]]
name = "windmill-audit"
version = "1.69.0"
version = "1.72.0"
dependencies = [
"chrono",
"serde",
@@ -4876,7 +4822,7 @@ dependencies = [
[[package]]
name = "windmill-common"
version = "1.69.0"
version = "1.72.0"
dependencies = [
"anyhow",
"axum",
@@ -4901,7 +4847,7 @@ dependencies = [
[[package]]
name = "windmill-parser"
version = "1.69.0"
version = "1.72.0"
dependencies = [
"serde",
"serde_json",
@@ -4909,7 +4855,7 @@ dependencies = [
[[package]]
name = "windmill-parser-bash"
version = "1.69.0"
version = "1.72.0"
dependencies = [
"anyhow",
"itertools",
@@ -4923,7 +4869,7 @@ dependencies = [
[[package]]
name = "windmill-parser-go"
version = "1.69.0"
version = "1.72.0"
dependencies = [
"anyhow",
"itertools",
@@ -4935,7 +4881,7 @@ dependencies = [
[[package]]
name = "windmill-parser-py"
version = "1.69.0"
version = "1.72.0"
dependencies = [
"anyhow",
"itertools",
@@ -4950,7 +4896,7 @@ dependencies = [
[[package]]
name = "windmill-parser-ts"
version = "1.69.0"
version = "1.72.0"
dependencies = [
"anyhow",
"deno_core",
@@ -4964,7 +4910,7 @@ dependencies = [
[[package]]
name = "windmill-queue"
version = "1.69.0"
version = "1.72.0"
dependencies = [
"anyhow",
"chrono",
@@ -4987,7 +4933,7 @@ dependencies = [
[[package]]
name = "windmill-worker"
version = "1.69.0"
version = "1.72.0"
dependencies = [
"anyhow",
"async-recursion",
@@ -5101,6 +5047,15 @@ version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
[[package]]
name = "winnow"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "faf09497b8f8b5ac5d3bb4d05c0a99be20f26fd3d5f2db7b0716e946d5103658"
dependencies = [
"memchr",
]
[[package]]
name = "winreg"
version = "0.10.1"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -550,21 +550,6 @@
},
"query": "SELECT * FROM workspace LIMIT $1 OFFSET $2"
},
"15de975d9be141c9ed9647935a508492aabbbddbf986d5c5c0f0c415293c432d": {
"describe": {
"columns": [],
"nullable": [],
"parameters": {
"Left": [
"Varchar",
"Varchar",
"Varchar",
"Varchar"
]
}
},
"query": "INSERT INTO variable\n (workspace_id, path, value, is_secret, description)\n VALUES ($1, 'g/all/pretty_secret', $2, true, 'This item is secret'), \n ($3, 'g/all/not_secret', $4, false, 'This item is not secret')"
},
"163f00eb8b1a489d5f382cdba22a5744e88a8e6f1532d7cb02af560f5f5d49f7": {
"describe": {
"columns": [
@@ -660,6 +645,27 @@
},
"query": "SELECT * FROM workspace_settings WHERE workspace_id = $1"
},
"176afff6dfceac701d499663f15fb7f569fd9d449406df4c5ecc811414429d42": {
"describe": {
"columns": [
{
"name": "flow_status",
"ordinal": 0,
"type_info": "Jsonb"
}
],
"nullable": [
null
],
"parameters": {
"Left": [
"Uuid",
"Text"
]
}
},
"query": "SELECT flow_status FROM completed_job WHERE id = $1 AND workspace_id = $2 \n UNION ALL SELECT flow_status FROM queue WHERE id = $1 AND workspace_id = $2 "
},
"187a27d18f78f068319a0ec684f71a76c49dd09c5c74c2c777e35233c4f5a281": {
"describe": {
"columns": [
@@ -1183,33 +1189,6 @@
},
"query": "UPDATE script SET archived = true WHERE hash = $1 AND workspace_id = $2"
},
"2be0cfd075df9624ccbcbe5fd645e0a5c25460c2d01493f86dcdd9b2b71f6181": {
"describe": {
"columns": [
{
"name": "flow_status",
"ordinal": 0,
"type_info": "Jsonb"
},
{
"name": "parent_job",
"ordinal": 1,
"type_info": "Uuid"
}
],
"nullable": [
null,
null
],
"parameters": {
"Left": [
"Uuid",
"Text"
]
}
},
"query": "SELECT flow_status, parent_job FROM completed_job WHERE id = $1 AND workspace_id = $2 UNION ALL SELECT flow_status, parent_job FROM queue WHERE id = $1 AND workspace_id = $2 "
},
"2e4115bb2e6c8c85ad1492ad135d6b0454b342126cb5fa17e58caf71b32ee755": {
"describe": {
"columns": [],
@@ -3678,6 +3657,33 @@
},
"query": "SELECT is_admin FROM usr where username = $1 AND workspace_id = $2 AND disabled = false"
},
"92ce38eaaef9e793a49788bf3b7fc88340e83da57b785364a477f08c776f66f3": {
"describe": {
"columns": [
{
"name": "flow_status",
"ordinal": 0,
"type_info": "Jsonb"
},
{
"name": "parent_job",
"ordinal": 1,
"type_info": "Uuid"
}
],
"nullable": [
null,
null
],
"parameters": {
"Left": [
"Uuid",
"Text"
]
}
},
"query": "SELECT flow_status, parent_job FROM completed_job WHERE id = $1 AND workspace_id = $2 \n UNION ALL SELECT flow_status, parent_job FROM queue WHERE id = $1 AND workspace_id = $2 "
},
"930ad84a4db26fa6d2c8447d447099e944a0ea7b1266b4d02cee620fe3d761a5": {
"describe": {
"columns": [],

View File

@@ -6,7 +6,7 @@
* LICENSE-AGPL for a copy of the license.
*/
use std::net::SocketAddr;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use git_version::git_version;
use sqlx::{Pool, Postgres};
@@ -15,6 +15,7 @@ use windmill_common::utils::rd_string;
const GIT_VERSION: &str = git_version!(args = ["--tag", "--always"], fallback = "unknown-version");
const DEFAULT_NUM_WORKERS: usize = 3;
const DEFAULT_PORT: u16 = 8000;
const DEFAULT_SERVER_BIND_ADDR: Ipv4Addr = Ipv4Addr::new(0, 0, 0, 0);
mod ee;
@@ -39,6 +40,11 @@ async fn main() -> anyhow::Result<()> {
.transpose()?
.flatten();
let server_bind_address: IpAddr = std::env::var("SERVER_BIND_ADDR")
.ok()
.and_then(|x| x.parse().ok() )
.unwrap_or(IpAddr::from(DEFAULT_SERVER_BIND_ADDR));
let port: u16 = std::env::var("PORT")
.ok()
.and_then(|x| x.parse::<u16>().ok())
@@ -60,8 +66,63 @@ async fn main() -> anyhow::Result<()> {
let (tx, rx) = tokio::sync::broadcast::channel::<()>(3);
let shutdown_signal = windmill_common::shutdown_signal(tx);
#[cfg(feature = "enterprise")]
tracing::info!(
"
##############################
Windmill Enterprise Edition {GIT_VERSION}
##############################"
);
#[cfg(not(feature = "enterprise"))]
tracing::info!(
"
##############################
Windmill Community Edition {GIT_VERSION}
##############################"
);
display_config(vec![
"DISABLE_NSJAIL",
"DISABLE_SERVER",
"NUM_WORKERS",
"METRICS_ADDR",
"JSON_FMT",
"BASE_URL",
"BASE_INTERNAL_URL",
"TIMEOUT",
"SLEEP_QUEUE",
"MAX_LOG_SIZE",
"SERVER_BIND_ADDR",
"PORT",
"KEEP_JOB_DIR",
"S3_CACHE_BUCKET",
"TAR_CACHE_RATE",
"COOKIE_DOMAIN",
"PYTHON_PATH",
"DENO_PATH",
"GO_PATH",
"PIP_INDEX_URL",
"PIP_EXTRA_INDEX_URL",
"PIP_TRUSTED_HOST",
"PATH",
"HOME",
"DATABASE_CONNECTIONS",
"TIMEOUT_WAIT_RESULT",
"QUEUE_LIMIT_WAIT_RESULT",
"DENO_AUTH_TOKENS",
"DENO_FLAGS",
"PIP_LOCAL_DEPENDENCIES",
"ADDITIONAL_PYTHON_PATHS",
"INCLUDE_HEADERS",
"WHITELIST_WORKSPACES",
"BLACKLIST_WORKSPACES",
"NEW_USER_WEBHOOK",
"CLOUD_HOSTED",
]);
if server_mode || num_workers > 0 {
let addr = SocketAddr::from(([0, 0, 0, 0], port));
let addr = SocketAddr::from((server_bind_address, port));
let server_f = async {
if server_mode {
@@ -72,60 +133,6 @@ async fn main() -> anyhow::Result<()> {
let workers_f = async {
if num_workers > 0 {
#[cfg(feature = "enterprise")]
tracing::info!(
"
##############################
Windmill Enterprise Edition {GIT_VERSION}
##############################"
);
#[cfg(not(feature = "enterprise"))]
tracing::info!(
"
##############################
Windmill Community Edition {GIT_VERSION}
##############################"
);
display_config(vec![
"DISABLE_NSJAIL",
"DISABLE_SERVER",
"NUM_WORKERS",
"METRICS_ADDR",
"JSON_FMT",
"BASE_URL",
"BASE_INTERNAL_URL",
"TIMEOUT",
"SLEEP_QUEUE",
"MAX_LOG_SIZE",
"PORT",
"KEEP_JOB_DIR",
"S3_CACHE_BUCKET",
"TAR_CACHE_RATE",
"COOKIE_DOMAIN",
"PYTHON_PATH",
"DENO_PATH",
"GO_PATH",
"PIP_INDEX_URL",
"PIP_EXTRA_INDEX_URL",
"PIP_TRUSTED_HOST",
"PATH",
"HOME",
"DATABASE_CONNECTIONS",
"TIMEOUT_WAIT_RESULT",
"QUEUE_LIMIT_WAIT_RESULT",
"DENO_AUTH_TOKENS",
"DENO_FLAGS",
"PIP_LOCAL_DEPENDENCIES",
"ADDITIONAL_PYTHON_PATHS",
"INCLUDE_HEADERS",
"WHITELIST_WORKSPACES",
"BLACKLIST_WORKSPACES",
"NEW_USER_WEBHOOK",
"CLOUD_HOSTED",
]);
run_workers(
db.clone(),
rx.resubscribe(),

View File

@@ -1,7 +1,7 @@
openapi: "3.0.3"
info:
version: 1.69.0
version: 1.72.0
title: Windmill API
contact:
@@ -3337,6 +3337,22 @@ paths:
schema:
$ref: "#/components/schemas/CompletedJob"
/w/{workspace}/jobs/completed/get_result/{id}:
get:
summary: get completed job result
operationId: getCompletedJobResult
tags:
- job
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/JobId"
responses:
"200":
description: result
content:
application/json:
schema: {}
/w/{workspace}/jobs/completed/delete/{id}:
post:
summary: delete completed job (erase content but keep run id)

View File

@@ -7,11 +7,12 @@
*/
use axum::{
extract::{Extension, Path},
extract::{Extension, Path, Query},
routing::{get, post, put},
Json, Router,
};
use hyper::StatusCode;
use hyper::{HeaderMap, StatusCode};
use serde::Deserialize;
use windmill_common::{
error::{JsonResult, Result},
utils::{not_found_if_none, StripPath},
@@ -19,6 +20,7 @@ use windmill_common::{
use crate::{
db::{UserDB, DB},
jobs::add_include_headers,
users::Authed,
};
@@ -83,13 +85,21 @@ pub async fn new_payload(
Ok(StatusCode::CREATED)
}
#[derive(Deserialize, Clone)]
pub struct IncludeHeaderQuery {
include_header: Option<String>,
}
pub async fn update_payload(
Extension(db): Extension<DB>,
Path((w_id, path)): Path<(String, StripPath)>,
Json(payload): Json<serde_json::Value>,
Query(run_query): Query<IncludeHeaderQuery>,
headers: HeaderMap,
Json(args): Json<Option<serde_json::Map<String, serde_json::Value>>>,
) -> Result<StatusCode> {
let mut tx = db.begin().await?;
let args = add_include_headers(&run_query.include_header, headers, args.unwrap_or_default());
sqlx::query!(
"
UPDATE capture
@@ -99,7 +109,7 @@ pub async fn update_payload(
",
&w_id,
&path.to_path(),
&payload,
serde_json::json!(args),
)
.execute(&mut tx)
.await?;

View File

@@ -295,28 +295,38 @@ impl RunJobQuery {
fn add_include_headers(
&self,
headers: HeaderMap,
mut args: serde_json::Map<String, serde_json::Value>,
args: serde_json::Map<String, serde_json::Value>,
) -> serde_json::Map<String, serde_json::Value> {
let whitelist = self
.include_header
.as_ref()
.map(|s| s.split(",").map(|s| s.to_string()).collect::<Vec<_>>())
.unwrap_or_default();
whitelist
.iter()
.chain(INCLUDE_HEADERS.iter())
.for_each(|h| {
if let Some(v) = headers.get(h) {
args.insert(
h.to_string().to_lowercase().replace('-', "_"),
serde_json::Value::String(v.to_str().unwrap().to_string()),
);
}
});
args
return add_include_headers(&self.include_header, headers, args);
}
}
pub fn add_include_headers(
include_header: &Option<String>,
headers: HeaderMap,
mut args: serde_json::Map<String, serde_json::Value>,
) -> serde_json::Map<String, serde_json::Value> {
if include_header.is_none() {
return args;
}
let whitelist = include_header
.as_ref()
.map(|s| s.split(",").map(|s| s.to_string()).collect::<Vec<_>>())
.unwrap_or_default();
whitelist
.iter()
.chain(INCLUDE_HEADERS.iter())
.for_each(|h| {
if let Some(v) = headers.get(h) {
args.insert(
h.to_string().to_lowercase().replace('-', "_"),
serde_json::Value::String(v.to_str().unwrap().to_string()),
);
}
});
args
}
#[derive(Deserialize)]
pub struct ListQueueQuery {
pub script_path_start: Option<String>,
@@ -1775,6 +1785,7 @@ async fn get_completed_job(
.fetch_optional(&db)
.await?;
tracing::info!("job_o: {:?}", job_o);
let job = not_found_if_none(job_o, "Completed Job", id.to_string())?;
Ok(Json(job))
}

View File

@@ -1311,7 +1311,9 @@ async fn create_user(
if let Some(new_user_webhook) = NEW_USER_WEBHOOK.clone() {
let _ = HTTP_CLIENT
.post(&new_user_webhook)
.json(&serde_json::json!({"email" : &nu.email, "name": &nu.name, "event": "new_user"}))
.json(
&serde_json::json!({"email" : &nu.email, "name": &nu.name, "event": "global_add"}),
)
.send()
.await
.map_err(|e| tracing::error!("Error sending new user webhook: {}", e.to_string()));

View File

@@ -6,10 +6,10 @@
* LICENSE-AGPL for a copy of the license.
*/
#[cfg(enterprise)]
#[cfg(feature = "enterprise")]
use std::str::FromStr;
#[cfg(enterprise)]
#[cfg(feature = "enterprise")]
use crate::BASE_URL;
use crate::{
apps::AppWithLastVersion,
@@ -20,7 +20,7 @@ use crate::{
utils::require_super_admin,
HTTP_CLIENT,
};
#[cfg(enterprise)]
#[cfg(feature = "enterprise")]
use axum::response::Redirect;
use axum::{
body::StreamBody,
@@ -30,7 +30,7 @@ use axum::{
routing::{delete, get, post},
Json, Router,
};
#[cfg(enterprise)]
#[cfg(feature = "enterprise")]
use stripe::CustomerId;
use windmill_audit::{audit_log, ActionKind};
use windmill_common::{
@@ -63,12 +63,13 @@ pub fn workspaced_service() -> Router {
.route("/tarball", get(tarball_workspace))
.route("/premium_info", get(premium_info));
#[cfg(enterprise)]
let router = {
router
.route("/checkout", get(stripe_checkout))
.route("/billing_portal", get(stripe_portal));
};
#[cfg(feature = "enterprise")]
tracing::info!("stripe enabled");
#[cfg(feature = "enterprise")]
let router = router
.route("/checkout", get(stripe_checkout))
.route("/billing_portal", get(stripe_portal));
router
}
@@ -230,13 +231,13 @@ async fn premium_info(
Ok(Json(row))
}
#[cfg(enterprise)]
#[cfg(feature = "enterprise")]
#[derive(Deserialize)]
struct PlanQuery {
plan: String,
}
#[cfg(enterprise)]
#[cfg(feature = "enterprise")]
async fn stripe_checkout(
authed: Authed,
Path(w_id): Path<String>,
@@ -302,7 +303,7 @@ async fn stripe_checkout(
}
}
#[cfg(enterprise)]
#[cfg(feature = "enterprise")]
async fn stripe_portal(
authed: Authed,
Path(w_id): Path<String>,
@@ -964,7 +965,7 @@ async fn invite_user(
if let Some(new_user_webhook) = NEW_USER_WEBHOOK.clone() {
let _ = &HTTP_CLIENT
.post(&new_user_webhook)
.json(&serde_json::json!({"email" : &nu.email, "event": "new_invite"}))
.json(&serde_json::json!({"email" : &nu.email, "event": "workspace_invite"}))
.send()
.await
.map_err(|e| tracing::error!("Error sending new user webhook: {}", e.to_string()));
@@ -1001,6 +1002,15 @@ async fn add_user(
tx.commit().await?;
if let Some(new_user_webhook) = NEW_USER_WEBHOOK.clone() {
let _ = HTTP_CLIENT
.post(&new_user_webhook)
.json(&serde_json::json!({"email" : &nu.email, "event": "workspace_add"}))
.send()
.await
.map_err(|e| tracing::error!("Error sending new user webhook: {}", e.to_string()));
}
Ok((
StatusCode::CREATED,
format!("user with email {} added", nu.email),

View File

@@ -6,7 +6,10 @@
* LICENSE-AGPL for a copy of the license.
*/
use std::{collections::HashMap, str::FromStr};
use std::{
collections::{HashMap, VecDeque},
str::FromStr,
};
use anyhow::Context;
use reqwest::Client;
@@ -17,7 +20,7 @@ use ulid::Ulid;
use uuid::Uuid;
use windmill_audit::{audit_log, ActionKind};
use windmill_common::{
error::{self, Error},
error::{self, to_anyhow, Error},
flow_status::{FlowStatus, JobResult, MAX_RETRY_ATTEMPTS, MAX_RETRY_INTERVAL},
flows::{FlowModule, FlowModuleValue, FlowValue},
scripts::{get_full_hub_script_by_path, HubScript, ScriptHash, ScriptLang},
@@ -150,6 +153,41 @@ pub async fn pull(
Ok(job)
}
pub async fn find_recursively_downward(
db: &Pool<Postgres>,
w_id: &str,
flow_id: Uuid,
node_id: &str,
) -> windmill_common::error::Result<Option<JobResult>> {
let mut bfs_stack = VecDeque::new();
bfs_stack.push_back(flow_id);
while bfs_stack.len() > 0 {
let parent_id = bfs_stack.pop_front().unwrap();
let job = sqlx::query_scalar!(
"SELECT flow_status FROM completed_job WHERE id = $1 AND workspace_id = $2
UNION ALL SELECT flow_status FROM queue WHERE id = $1 AND workspace_id = $2 ",
parent_id,
w_id
)
.fetch_optional(db)
.await?
.flatten();
if let Some(r) = job {
let status = serde_json::from_value::<FlowStatus>(r).map_err(to_anyhow)?;
for m in status.modules.iter() {
let id = m.id();
if id == node_id {
return Ok(m.job_result());
}
if let Some(job_id) = m.job() {
bfs_stack.push_back(job_id);
}
}
}
}
Ok(None)
}
pub async fn get_result_by_id(
db: Pool<Postgres>,
mut skip_direct: bool,
@@ -159,10 +197,12 @@ pub async fn get_result_by_id(
) -> error::Result<serde_json::Value> {
let mut result_id: Option<JobResult> = None;
let mut parent_id = Uuid::from_str(&flow_id).ok();
let mut lparent_id = parent_id.clone();
while result_id.is_none() && parent_id.is_some() {
if !skip_direct {
let r = sqlx::query!(
"SELECT flow_status, parent_job FROM completed_job WHERE id = $1 AND workspace_id = $2 UNION ALL SELECT flow_status, parent_job FROM queue WHERE id = $1 AND workspace_id = $2 ",
"SELECT flow_status, parent_job FROM completed_job WHERE id = $1 AND workspace_id = $2
UNION ALL SELECT flow_status, parent_job FROM queue WHERE id = $1 AND workspace_id = $2 ",
parent_id.unwrap(),
w_id,
)
@@ -174,6 +214,7 @@ pub async fn get_result_by_id(
.as_ref()
.ok_or_else(|| Error::InternalErr(format!("requiring a flow status value")))?
.to_owned();
lparent_id = parent_id;
parent_id = r.parent_job;
let status_o = serde_json::from_value::<FlowStatus>(value).ok();
result_id = status_o.and_then(|status| {
@@ -195,10 +236,17 @@ pub async fn get_result_by_id(
.fetch_optional(&db)
.await?
.flatten();
lparent_id = parent_id;
parent_id = q_parent;
skip_direct = false
}
}
// we could not find the node going upward from the flow by looking at all the jobs (in progress or completed)
// we now look downward from the flow root to the all the children completed job for a job that might hide itself
// in a deep non-direct parent job such as in nested branches
if result_id.is_none() && lparent_id.is_some() {
result_id = find_recursively_downward(&db, &w_id, lparent_id.unwrap(), &node_id).await?;
}
let result_id = windmill_common::utils::not_found_if_none(
result_id,
"Flow result by id",
@@ -351,7 +399,10 @@ pub async fn push<'c>(
.unwrap_or(false);
if !is_super_admin {
if usage > MAX_FREE_EXECS {
if usage > MAX_FREE_EXECS
&& !matches!(job_payload, JobPayload::Dependencies { .. })
&& !matches!(job_payload, JobPayload::FlowDependencies { .. })
{
return Err(error::Error::BadRequest(format!(
"User {email} has exceeded the free usage limit of {MAX_FREE_EXECS} that applies outside of premium workspaces."
)));
@@ -466,10 +517,10 @@ pub async fn push<'c>(
}
JobPayload::Flow(flow) => {
let value_json = sqlx::query_scalar!(
"SELECT value FROM flow WHERE path = $1 AND workspace_id = $2",
flow,
workspace_id
)
"SELECT value FROM flow WHERE path = $1 AND workspace_id = $2",
flow,
workspace_id
)
.fetch_optional(&mut tx)
.await?
.ok_or_else(|| Error::InternalErr(format!("not found flow at path {:?}", flow)))?;

View File

@@ -145,7 +145,7 @@ fn add_closing_bracket(s: &str) -> String {
s
}
const SPLIT_PAT: &str = ";\n";
const SPLIT_PAT: &str = ";";
async fn eval(
context: &mut JsRuntime,
expr: &str,
@@ -154,14 +154,21 @@ async fn eval(
by_id: Option<IdContext>,
base_internal_url: &str,
) -> anyhow::Result<serde_json::Value> {
let expr = expr.trim();
let expr = format!(
"{}\nreturn {};",
expr.split(SPLIT_PAT)
.take(expr.split(SPLIT_PAT).count() - 1)
.join("\n"),
expr.split(SPLIT_PAT).last().unwrap_or_else(|| "")
);
let exprs = expr
.trim()
.split(SPLIT_PAT)
.map(|x| x.trim())
.filter(|x| !x.is_empty())
.collect::<Vec<&str>>();
let expr = if exprs.is_empty() {
"return undefined;".to_string()
} else {
format!(
"{};\n return {};",
exprs.iter().take(exprs.len() - 1).join(";\n"),
exprs.last().unwrap()
)
};
let (api_code, by_id_code) = if let Some(EvalCreds { workspace, token }) = creds {
let by_id_code = if let Some(by_id) = by_id {
format!(
@@ -198,12 +205,12 @@ const results = new Proxy({{}}, {{
.into_iter()
.map(|(k, v)| {
let v_str = match v {
JobResult::SingleJob(x) => x.to_string(),
JobResult::SingleJob(x) => format!("\"{x}\""),
JobResult::ListJob(x) => {
format!("[{}]", x.iter().map(|x| x.to_string()).join(","))
format!("[{}]", x.iter().map(|x| format!("\"{x}\"")).join(","))
}
};
format!("\"{k}\": \"{v_str}\"")
format!("\"{k}\": {v_str}")
})
.join(","),
by_id.previous_id,
@@ -292,9 +299,8 @@ async fn op_get_result(args: Vec<String>) -> Result<serde_json::Value, anyhow::E
let base_url = &args[3];
let client = windmill_api_client::create_client(base_url, token.clone());
let result = client
.get_completed_job(workspace, &id.parse()?)
.get_completed_job_result(workspace, &id.parse()?)
.await?
.result
.clone();
Ok(serde_json::json!(result))
}

View File

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

View File

@@ -23,7 +23,7 @@ async function tryResolveWorkspace(
{ isError: false; value: Workspace } | { isError: true; error: string }
> {
const cache = (opts as any).__secret_workspace;
if (cache) return cache;
if (cache) return { isError: false, value: cache };
if (opts.workspace) {
const e = await getWorkspaceByName(opts.workspace);
@@ -53,7 +53,6 @@ export async function resolveWorkspace(
): Promise<Workspace> {
const res = await tryResolveWorkspace(opts);
if (res.isError) {
console.log(res.error);
return Deno.exit(-1);
} else {
return res.value;
@@ -62,8 +61,8 @@ export async function resolveWorkspace(
export async function requireLogin(opts: GlobalOptions): Promise<GlobalUserInfo> {
const workspace = await resolveWorkspace(opts);
let token = await tryGetLoginInfo(opts);
if (!token) {
token = workspace.token;
}
@@ -80,9 +79,9 @@ export async function requireLogin(opts: GlobalOptions): Promise<GlobalUserInfo>
if (!newToken) {
throw new Error("Could not reauth");
}
removeWorkspace(workspace.name);
removeWorkspace(workspace.name, false, opts);
workspace.token = newToken;
addWorkspace(workspace);
addWorkspace(workspace, opts);
setClient(
token,

View File

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

View File

@@ -192,7 +192,7 @@ async function list(opts: GlobalOptions & { showArchived?: boolean }) {
}
async function run(
opts: GlobalOptions & {
input: string[];
data?: string;
silent: boolean;
},
path: string,
@@ -200,7 +200,8 @@ async function run(
const workspace = await resolveWorkspace(opts);
await requireLogin(opts);
const input = await resolve(opts.input);
const input = opts.data ? await resolve(opts.data) : {};
const id = await JobService.runFlowByPath({
workspace: workspace.workspaceId,
@@ -236,6 +237,7 @@ async function run(
if (!opts.silent) {
console.log(colors.green.underline.bold("Flow ran to completion"));
console.log()
}
const jobInfo = await JobService.getCompletedJob({
workspace: workspace.workspaceId,
@@ -257,8 +259,8 @@ const command = new Command()
.command("run", "run a flow by path.")
.arguments("<path:string>")
.option(
"-i --input [inputs...:string]",
"Inputs specified as JSON objects or simply as <name>=<value>. Supports file inputs using @<filename> and stdin using @- these also need to be formatted as JSON. Later inputs override earlier ones.",
"-d --data <data:string>",
"Inputs specified as a JSON string or a file using @<filename> or stdin using @-.",
)
.option(
"-s --silent",

View File

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

View File

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

View File

@@ -11,6 +11,7 @@ import {
Table,
} from "./deps.ts";
import { Any, array, decoverto, model, property } from "./decoverto.ts";
import { writeAllSync } from "https://deno.land/std@0.176.0/streams/mod.ts";
@model()
export class ScriptFile {
@@ -280,50 +281,27 @@ async function list(opts: GlobalOptions & { showArchived?: boolean }) {
.render();
}
export async function resolve(inputs: string[]): Promise<Record<string, any>> {
let result = {};
if (!inputs) {
return result;
export async function resolve(input: string): Promise<Record<string, any>> {
if (!input) {
throw new Error("No data given");
}
for (const input of inputs) {
let data: string;
if (input.startsWith("@")) {
if (input == "@-") {
data = new TextDecoder().decode(await readAll(Deno.stdin));
} else {
data = await Deno.readTextFile(input.substring(1));
}
} else {
if (input.startsWith("{")) {
data = input;
} else {
const key = input.split("=", 1)[0];
const value = input.substring(key.length + 1);
let o;
try {
o = JSON.parse(value);
} catch {
o = value;
}
data = JSON.stringify(Object.fromEntries([[key, o]]));
}
}
let jsonObj;
try {
jsonObj = JSON.parse(data);
} catch {
jsonObj = data;
}
result = { ...result, ...jsonObj };
if (input == "@-") {
input = new TextDecoder().decode(await readAll(Deno.stdin));
} if (input[0] == "@") {
input = await Deno.readTextFile(input.substring(1));
}
try {
return JSON.parse(input);
} catch (e) {
console.error("Impossible to parse input as JSON", input)
throw e
}
return result;
}
async function run(
opts: GlobalOptions & {
input: string[];
data?: string;
silent: boolean;
},
path: string,
@@ -331,7 +309,8 @@ async function run(
const workspace = await resolveWorkspace(opts);
await requireLogin(opts);
const input = await resolve(opts.input);
const input = opts.data ? await resolve(opts.data) : {};
const id = await JobService.runScriptByPath({
workspace: workspace.workspaceId,
path,
@@ -364,7 +343,9 @@ export async function track_job(workspace: string, id: string) {
const result = await JobService.getCompletedJob({ workspace, id });
console.log(result.logs);
console.log()
console.log(colors.bold.underline.green("Job Completed"));
console.log()
return;
} catch {
/* ignore */
@@ -403,7 +384,7 @@ export async function track_job(workspace: string, id: string) {
}
if (updates.new_logs) {
console.log(updates.new_logs);
writeAllSync(Deno.stdout, new TextEncoder().encode(updates.new_logs));
logOffset += updates.new_logs.length;
}
@@ -426,12 +407,15 @@ export async function track_job(workspace: string, id: string) {
if ((final_job.logs?.length ?? -1) > logOffset) {
console.log(final_job.logs!.substring(logOffset));
}
console.log("\n")
if (final_job.success) {
console.log(colors.bold.underline.green("Job Completed"));
} else {
console.log(colors.bold.underline.red("Job Completed"));
}
console.log()
} catch {
console.log("Job appears to have completed, but no data can be retrieved");
}
@@ -466,8 +450,8 @@ const command = new Command()
.command("run", "run a script by path")
.arguments("<path:string>")
.option(
"-i --input [inputs...:string]",
"Inputs specified as JSON objects or simply as <name>=<value>. Supports file inputs using @<filename> and stdin using @- these also need to be formatted as JSON. Later inputs override earlier ones.",
"-d --data <data:string>",
"Inputs specified as a JSON string or a file using @<filename> or stdin using @-.",
)
.option(
"-s --silent",

View File

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

View File

@@ -9,9 +9,11 @@ import {
Input,
setClient,
Table,
UserService,
WorkspaceService,
} from "./deps.ts";
import { decoverto, model, property } from "./decoverto.ts";
import { requireLogin } from "./context.ts";
@model()
export class Workspace {
@@ -308,12 +310,18 @@ async function remove(_opts: GlobalOptions, name: string) {
await removeWorkspace(name, false, _opts);
}
async function whoami(_opts: GlobalOptions) {
await requireLogin(_opts)
console.log(await UserService.globalWhoami())
}
const command = new Command()
.description("workspace related commands")
.action(list as any)
.command("switch")
.complete("workspace", async () => (await allWorkspaces()).map((x) => x.name))
.description("Switch to another workspace")
.arguments("<workspace_name:string>")
.arguments("<workspace_name:string:workspace>")
.action(switchC as any)
.command("add")
.description("Add a workspace")
@@ -334,6 +342,9 @@ const command = new Command()
.command("remove")
.description("Remove a workspace")
.arguments("<workspace_name:string>")
.action(remove as any);
.action(remove as any)
.command("whoami")
.description("Show the currently active user")
.action(whoami as any);
export default command;

View File

@@ -4,8 +4,9 @@ set -e
npm ci --ignore-scripts
npx --yes openapi-typescript-codegen --input ../backend/windmill-api/openapi.yaml \
--output ./src --useOptions \
&& sed -i '213 i \\ request.referrerPolicy = \"no-referrer\"\n' src/core/request.ts
npx --yes denoify
&& sed -i '213 i \\ request.referrerPolicy = \"no-referrer\"\n' src/core/request.ts
npx --yes denoify
rm -rf windmill-api
mv deno_dist windmill-api
sed -i 's/buffer DENOIFY: UNKNOWN NODE BUILTIN/node:buffer/' ./windmill-api/core/request.ts
rm -rf src/

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "windmill",
"version": "1.69.0",
"version": "1.72.0",
"scripts": {
"dev": "vite dev",
"build": "vite build",
@@ -15,7 +15,7 @@
"test": "playwright test --config=tests-out/playwright.config.js"
},
"devDependencies": {
"@playwright/test": "^1.29.2",
"@playwright/test": "^1.31.1",
"@sveltejs/adapter-static": "^1.0.0",
"@sveltejs/kit": "^1.0.0-next.589",
"@sveltejs/package": "^1.0.2",
@@ -25,7 +25,7 @@
"@types/vscode": "~1.74.0",
"@typescript-eslint/eslint-plugin": "^5.49.0",
"@typescript-eslint/parser": "^5.48.0",
"@windmill-labs/svelte-grid": "^5.1.3",
"@windmill-labs/svelte-grid": "^5.1.6",
"@windmill-labs/svelvet": "^4.0.20",
"@zerodevx/svelte-toast": "^0.8.1",
"autoprefixer": "^10.4.13",
@@ -34,6 +34,7 @@
"eslint": "^8.28.0",
"eslint-config-prettier": "^8.6.0",
"eslint-plugin-svelte3": "^4.0.0",
"ol": "^7.2.2",
"openapi-typescript-codegen": "^0.23.0",
"path-browserify": "^1.0.1",
"postcss": "^8.4.18",
@@ -66,11 +67,13 @@
"@popperjs/core": "^2.11.6",
"@redocly/json-to-json-schema": "^0.0.1",
"@tanstack/svelte-table": "^8.7.6",
"ag-grid-svelte": "^0.1.4",
"async-mutex": "^0.4.0",
"chart.js": "^3.9.1",
"chartjs-adapter-date-fns": "^3.0.0",
"chartjs-plugin-zoom": "^2.0.0",
"date-fns": "^2.29.3",
"fast-equals": "^4.0.3",
"highlight.js": "^11.7.0",
"lucide-svelte": "^0.115.0",
"monaco-editor": "0.33.0",
@@ -80,7 +83,7 @@
"svelte-autosize": "^1.0.1",
"svelte-chartjs": "^3.1.0",
"svelte-portal": "^2.2.0",
"svelte-select": "^5.0.2",
"svelte-select": "^5.3.1",
"tailwind-merge": "^1.9.1",
"vscode-ws-jsonrpc": "^2.0.1"
},

View File

@@ -44,9 +44,10 @@ declare module '@windmill-labs/svelte-grid' {
fastStart?: boolean
throttleUpdate?: number
throttleResize?: number
onTopId?: string
scroller?: undefined
sensor?: number
parentWidth?: number
}
export interface Slots<T> {
@@ -60,7 +61,7 @@ declare module '@windmill-labs/svelte-grid' {
mount: CustomEvent<>
},
Slots<T>
> {}
> { }
}
declare module '@windmill-labs/svelte-grid/build/helper/index.mjs' {

View File

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

View File

@@ -138,8 +138,18 @@
export function setCode(ncode: string): void {
code = ncode
if (editor) {
editor.setValue(ncode)
if (editor?.getModel()) {
// editor.setValue(ncode)
editor.pushUndoStop()
editor.executeEdits('set', [
{
range: editor.getModel()!.getFullModelRange(), // full range
text: ncode
}
])
editor.pushUndoStop()
}
}

View File

@@ -13,6 +13,7 @@
import UnsavedConfirmationModal from './common/confirmationModal/UnsavedConfirmationModal.svelte'
import { OFFSET } from './CronInput.svelte'
import FlowGraphViewer from './FlowGraphViewer.svelte'
import ScriptEditorDrawer from './flows/content/ScriptEditorDrawer.svelte'
import FlowEditor from './flows/FlowEditor.svelte'
import { flowStateStore } from './flows/flowState'
import { flowStore } from './flows/flowStore'
@@ -112,7 +113,6 @@
await createSchedule(flow.path)
}
}
sendUserToast(`Flow saved at ${$flowStore.path}`)
goto(`/flows/get/${$flowStore.path}?workspace_id=${$workspaceStore}`)
}
@@ -145,6 +145,7 @@
const scheduleStore = writable<Schedule>({ args: {}, cron: '', enabled: false })
const previewArgsStore = writable<Record<string, any>>(initialArgs)
const scriptEditorDrawer = writable<ScriptEditorDrawer | undefined>(undefined)
function select(selectedId: string) {
selectedIdStore.set(selectedId)
@@ -154,7 +155,8 @@
selectedId: selectedIdStore,
schedule: scheduleStore,
select,
previewArgs: previewArgsStore
previewArgs: previewArgsStore,
scriptEditorDrawer
})
async function loadSchedule() {
@@ -181,6 +183,7 @@
</script>
{#if !$userStore?.operator}
<ScriptEditorDrawer bind:this={$scriptEditorDrawer} />
<UnsavedConfirmationModal />
<Drawer bind:this={flowViewer} size="75%">

View File

@@ -97,7 +97,9 @@
class="w-full border-r border-b border-t border-gray-400 min-h-[150px] p-2 overflow-auto hidden sm:block"
>
{#if stepDetail == undefined}
<span class="font-black text-lg w-full my-4">
<SchemaViewer schema={flow?.schema} />
<span class="font-black text-lg w-full my-4 mt-14">
<span>Click on a step to see its details</span>
</span>
{:else if stepDetail == 'Input'}

View File

@@ -12,9 +12,9 @@
<div
class:border={!noBorder}
class="grid {!col ? 'grid-cols-2' : 'grid-rows-2'} shadow border-gray-400 h-full"
class="grid {!col ? 'grid-cols-2' : 'grid-rows-2'} shadow border-gray-400 h-full max-h-screen"
>
<div class="bg-white max-h-80 h-full p-1 overflow-auto relative">
<div class="bg-white {col ? '' : 'max-h-80'} h-full p-1 overflow-auto relative">
<span class="text-gray-500">Result</span>
{#if result}
<DisplayResult {result} />
@@ -24,7 +24,7 @@
<div class="text-gray-400">No result (result is undefined)</div>
{/if}
</div>
<div class="overflow-auto max-h-80 h-full relative">
<div class="overflow-auto {col ? '' : 'max-h-80'} h-full relative">
<LogViewer content={logs ?? ''} isLoading={false} />
</div>
</div>

View File

@@ -21,7 +21,6 @@
export let jobId: string | undefined = undefined
export let job: Job | undefined = undefined
let isValid: boolean = true
let isRunning: boolean = false
let jobProgressReset: () => void
@@ -105,7 +104,6 @@
{#if isRunning}
<Button
disabled={!isValid}
color="red"
on:click={async () => {
isRunning = false
@@ -132,7 +130,6 @@
color="blue"
size="sm"
btnClasses="w-full max-w-lg"
disabled={!isValid}
on:click={() => runPreview($previewArgs)}
>
Test flow <Kbd class="ml-2">Ctrl+Enter</Kbd>
@@ -149,14 +146,13 @@
</div>
<FlowProgressBar {job} bind:reset={jobProgressReset} />
<div class="overflow-y-auto grow divide-y divide-gray-600 ">
<div class="overflow-y-auto grow divide-y divide-gray-600 pr-4">
<div class="max-h-1/2 overflow-auto border-b border-gray-700">
<SchemaForm
noVariablePicker
compact
class="py-4 max-w-3xl"
schema={$flowStore.schema}
bind:isValid
bind:args={$previewArgs}
/>
</div>

View File

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

View File

@@ -82,16 +82,10 @@
}
function connectProperty(rawValue: string) {
if (isStaticTemplate(inputCat)) {
arg.value = `\$\{${rawValue}}`
setPropertyType(arg.value)
monacoTemplate?.setCode(arg.value)
} else {
arg.expr = getDefaultExpr(undefined, previousModuleId, rawValue)
arg.type = 'javascript'
propertyType = 'javascript'
monaco?.setCode(arg.expr)
}
arg.expr = getDefaultExpr(undefined, previousModuleId, rawValue)
arg.type = 'javascript'
propertyType = 'javascript'
monaco?.setCode(arg.expr)
}
function onFocus() {

View File

@@ -86,6 +86,7 @@
detailed={false}
topButton
bind:args={stepArgs}
isFlow={false}
/>
{#if testIsLoading}
<Button on:click={testJobLoader?.cancelJob} btnClasses="w-full mt-4" color="red" size="sm">

View File

@@ -277,15 +277,19 @@
folderCreated = undefined
}}
>
<div class="flex flex-row">
<input class="mr-2" placeholder="New folder name" bind:value={newFolderName} />
<Button size="md" startIcon={{ icon: faPlus }} disabled={!newFolderName} on:click={addFolder}>
New&nbsp;folder
</Button>
</div>
{#if folderCreated}
<div class="mt-8" />
{#if !folderCreated}
<div class="flex flex-row">
<input class="mr-2" placeholder="New folder name" bind:value={newFolderName} />
<Button
size="md"
startIcon={{ icon: faPlus }}
disabled={!newFolderName}
on:click={addFolder}
>
New&nbsp;folder
</Button>
</div>
{:else}
<FolderEditor name={folderCreated} />
{/if}
</DrawerContent>

View File

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

View File

@@ -49,6 +49,7 @@
export let loading = false
export let noVariablePicker = false
export let viewCliRun = false
export let isFlow: boolean
export let args: Record<string, any> = decodeArgs($page.url.searchParams.get('args') ?? undefined)
@@ -64,9 +65,9 @@
let scheduledForStr: string | undefined
let invisible_to_owner: false
$: cliCommand = `wmill ${runnable?.kind} run ${runnable?.path} ${Object.entries(args)
.map(([k, v]) => `-i ${k}=${JSON.stringify(v)}`)
.join(' ')}`
$: cliCommand = `wmill ${isFlow ? 'flow' : 'script'} run ${runnable?.path} -d '${JSON.stringify(
args
)}'`
</script>
<div class="max-w-6xl">
@@ -145,7 +146,7 @@
</Button>
</div>
{#if viewOptions}
<div transition:slide class="mt-6">
<div transition:slide|local class="mt-6">
<div class="border rounded-md p-3 pt-4">
<div class="flex flex-row items-end">
<div class="w-max md:w-2/3 mt-2 mb-1">
@@ -213,14 +214,14 @@
<div class="my-10" />
<Button
color="light"
size="sm"
size="xs"
endIcon={{ icon: viewCliOptions ? faChevronUp : faChevronDown }}
on:click={() => (viewCliOptions = !viewCliOptions)}
>
Run it from the CLI
</Button>
{#if viewCliOptions}
<div transition:slide class="mt-2 px-4 pt-2">
<div transition:slide|local class="mt-2 px-4 pt-2">
<InlineCodeCopy content={cliCommand} />
<CliHelpBox />
</div>

View File

@@ -4,7 +4,6 @@
import { workspaceStore } from '$lib/stores'
import { allTrue } from '$lib/utils'
import { faPlus } from '@fortawesome/free-solid-svg-icons'
import { slide } from 'svelte/transition'
import ArgInput from './ArgInput.svelte'
import { Button } from './common'
import InputTransformForm from './InputTransformForm.svelte'
@@ -69,8 +68,8 @@
<div class="w-full {clazz} {flexWrap ? 'flex flex-row flex-wrap gap-x-6 gap-y-2' : ''}">
{#if keys.length > 0}
{#each keys as argName, i (argName)}
{#if !filter || filter.includes(argName)}
<div transition:slide|local>
{#if (!filter || filter.includes(argName)) && Object.keys(schema.properties ?? {}).includes(argName)}
<div>
{#if inputTransform}
<InputTransformForm
{previousModuleId}
@@ -84,7 +83,7 @@
bind:pickForField
{noDynamicToggle}
/>
{:else if typeof args == 'object'}
{:else if typeof args == 'object' && schema?.properties[argName]}
<ArgInput
autofocus={i == 0 && autofocus}
label={argName}

View File

@@ -24,6 +24,7 @@
export let initialPath: string = ''
export let template: 'pgsql' | 'mysql' | 'script' = 'script'
export let initialArgs: Record<string, any> = {}
export let lockedLanguage = false
const langs: [string, SupportedLanguage][] = [
['Typescript', Script.language.DENO],
@@ -82,7 +83,6 @@
kind: script.kind
}
})
sendUserToast(`New script created at hash ${newHash}`)
history.replaceState(history.state, '', `/scripts/edit/${newHash}?step=2`)
goto(`/scripts/get/${newHash}?workspace_id=${$workspaceStore}`)
} catch (error) {
@@ -214,7 +214,13 @@
bind:value={script.summary}
placeholder="Short summary to be displayed when listed"
/>
<h2 class="border-b pb-1 mt-12 mb-6">Language</h2>
{#if lockedLanguage}
<div class="text-sm text-gray-600 italic mb-2">
As a forked script, the language '{script.language}' cannot be modified.
</div>
{/if}
<div class="flex flex-row gap-2 flex-wrap">
{#each langs as [label, lang]}
{@const isPicked = script.language == lang && template == 'script'}
@@ -228,6 +234,7 @@
template = 'script'
initContent(lang, script.kind, template)
}}
disabled={lockedLanguage}
>
<LanguageIcon {lang} /><span class="ml-2 py-4">{label}</span>
</Button>
@@ -237,6 +244,7 @@
variant="border"
color={template == 'pgsql' ? 'blue' : 'dark'}
btnClasses={template == 'pgsql' ? '!border-2 !bg-blue-50/75' : 'm-[1px]'}
disabled={lockedLanguage}
on:click={() => {
script.language = Script.language.DENO
template = 'pgsql'

View File

@@ -101,6 +101,7 @@
onMount(() => {
inferSchema(code)
loadPastTests()
})
</script>

View File

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

View File

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

View File

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

View File

@@ -39,6 +39,7 @@
export function closeDrawer() {
drawer?.closeDrawer()
const index = $page.url.href.lastIndexOf('#')
if (index === -1) return
const hashRemoved = $page.url.href.slice(0, index)
goto(hashRemoved)
}

View File

@@ -10,6 +10,7 @@
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
import { loadIcon } from '../icon'
import { twMerge } from 'tailwind-merge'
import { goto } from '$app/navigation'
export let id: string
export let componentInput: AppInput | undefined
@@ -32,7 +33,8 @@
let runnableComponent: RunnableComponent
let disabled: boolean | undefined = undefined
let fillContainer: boolean | undefined = undefined
let goto: string | undefined = undefined
let gotoUrl: string | undefined = undefined
let gotoNewTab: boolean | undefined = undefined
let isLoading: boolean = false
let ownClick: boolean = false
@@ -87,7 +89,7 @@
</script>
<InputValue {id} input={configuration.label} bind:value={labelValue} />
<InputValue {id} input={configuration.goto} bind:value={goto} />
<InputValue {id} input={configuration.goto} bind:value={gotoUrl} />
<InputValue {id} input={configuration.color} bind:value={color} />
<InputValue {id} input={configuration.size} bind:value={size} />
<InputValue {id} input={configuration.beforeIcon} bind:value={beforeIcon} />
@@ -95,22 +97,23 @@
<InputValue {id} input={configuration.triggerOnAppLoad} bind:value={triggerOnAppLoad} />
<InputValue
row={extraQueryParams['row']}
{id}
input={configuration.disabled}
bind:value={disabled}
bind:error={errors.disabled}
/>
<InputValue {id} input={configuration.fillContainer} bind:value={fillContainer} />
<InputValue {id} input={configuration.gotoNewTab} bind:value={gotoNewTab} />
<RunnableWrapper
flexWrap
bind:runnableComponent
bind:componentInput
{componentInput}
{id}
{extraQueryParams}
autoRefresh={false}
{goto}
goto={gotoUrl}
{gotoNewTab}
>
<AlignWrapper {noWFull} {horizontalAlignment} {verticalAlignment}>
{#if errorsMessage}
@@ -135,7 +138,18 @@
e?.stopPropagation()
e?.preventDefault()
ownClick = true
await runnableComponent?.runComponent()
if (!runnableComponent) {
if (gotoUrl) {
if (gotoNewTab) {
window.open(gotoUrl, '_blank')
} else {
goto(gotoUrl)
}
}
} else {
await runnableComponent?.runComponent()
}
if (recomputeIds) {
recomputeIds.forEach((id) => {

View File

@@ -5,7 +5,8 @@
import { Icon } from 'svelte-awesome'
import type { AppInput } from '../../inputType'
import type { Output } from '../../rx'
import type { AppEditorContext } from '../../types'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import { concatCustomCss } from '../../utils'
import AlignWrapper from '../helpers/AlignWrapper.svelte'
import InputValue from '../helpers/InputValue.svelte'
import type RunnableComponent from '../helpers/RunnableComponent.svelte'
@@ -17,10 +18,12 @@
export let recomputeIds: string[] | undefined = undefined
export let extraQueryParams: Record<string, any> = {}
export let horizontalAlignment: 'left' | 'center' | 'right' | undefined = undefined
export let customCss: ComponentCustomCSS<'container' | 'button'> | undefined = undefined
export const staticOutputs: string[] = ['loading', 'result']
const { runnableComponents, worldStore } = getContext<AppEditorContext>('AppEditorContext')
const { app, runnableComponents, worldStore, stateId } =
getContext<AppEditorContext>('AppEditorContext')
let labelValue: string = 'Default label'
let color: ButtonType.Color
@@ -31,7 +34,8 @@
let isLoading: boolean = false
$: noInputs =
componentInput?.type != 'runnable' || Object.keys(componentInput?.fields ?? {}).length == 0
$stateId != undefined &&
(componentInput?.type != 'runnable' || Object.keys(componentInput?.fields ?? {}).length == 0)
$: outputs = $worldStore?.outputsById[id] as {
result: Output<Array<any>>
@@ -47,6 +51,8 @@
isLoading = value
}
})
$: css = concatCustomCss($app.css?.formcomponent, customCss)
</script>
<InputValue {id} input={configuration.goto} bind:value={goto} />
@@ -55,18 +61,21 @@
<InputValue {id} input={configuration.size} bind:value={size} />
<RunnableWrapper
defaultUserInput
bind:runnableComponent
bind:componentInput
{componentInput}
{id}
{goto}
{extraQueryParams}
autoRefresh={false}
forceSchemaDisplay={true}
runnableClass="!block"
runnableStyle={css?.container.style}
>
<AlignWrapper {horizontalAlignment}>
<div class="flex flex-col gap-2 px-4 w-full">
<div
class="flex flex-col gap-2 px-4 w-full {css?.container?.class ?? ''}"
style={css?.container?.style ?? ''}
>
<div>
{#if noInputs}
<div class="text-gray-600 italic text-sm my-4">
@@ -84,7 +93,8 @@
{#if !noInputs}
<Button
loading={isLoading}
btnClasses="my-1"
btnClasses="my-1 {css?.button?.class ?? ''}"
style={css?.button?.style ?? ''}
on:pointerdown={(e) => {
e?.stopPropagation()
window.dispatchEvent(new Event('pointerup'))

View File

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

View File

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

View File

@@ -3,7 +3,11 @@
import { getContext } from 'svelte'
import { twMerge } from 'tailwind-merge'
import type { AppInput } from '../../inputType'
import { IS_APP_PUBLIC_CONTEXT_KEY, type AppEditorContext, type ComponentCustomCSS } from '../../types'
import {
IS_APP_PUBLIC_CONTEXT_KEY,
type AppEditorContext,
type ComponentCustomCSS
} from '../../types'
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
export let id: string
@@ -18,19 +22,23 @@
export const staticOutputs: string[] = ['result', 'loading']
</script>
<RunnableWrapper flexWrap bind: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',
$app.css?.['displaycomponent']?.['header']?.class,
customCss?.header?.class
)}>
<RunnableWrapper 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',
$app.css?.['displaycomponent']?.['header']?.class,
customCss?.header?.class
)}
>
Results
</div>
<div class={twMerge(
'p-2',
$app.css?.['displaycomponent']?.['container']?.class,
customCss?.container?.class
)}>
<div
class={twMerge(
'p-2',
$app.css?.['displaycomponent']?.['container']?.class,
customCss?.container?.class
)}
>
<DisplayResult {result} {requireHtmlApproval} />
</div>
</RunnableWrapper>

View File

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

View File

@@ -1,6 +1,9 @@
<script lang="ts">
import { getContext } from 'svelte'
import type { AppInput } from '../../inputType'
import { AlignWrapper, InputValue, RunnableWrapper } from '../helpers'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import { concatCustomCss } from '../../utils'
import { AlignWrapper, InputValue } from '../helpers'
import { loadIcon } from '../icon'
export let id: string
@@ -8,6 +11,9 @@
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
export let configuration: Record<string, AppInput>
export const staticOutputs: string[] = []
export let customCss: ComponentCustomCSS<'container' | 'icon'> | undefined = undefined
const { app } = getContext<AppEditorContext>('AppEditorContext')
let icon: string | undefined = undefined
let size: number
@@ -22,6 +28,8 @@
iconComponent = await loadIcon(icon)
}
}
$: css = concatCustomCss($app.css?.iconcomponent, customCss)
</script>
<InputValue {id} input={configuration.icon} bind:value={icon} />
@@ -29,13 +37,20 @@
<InputValue {id} input={configuration.color} bind:value={color} />
<InputValue {id} input={configuration.strokeWidth} bind:value={strokeWidth} />
<AlignWrapper {horizontalAlignment} {verticalAlignment}>
<AlignWrapper
{horizontalAlignment}
{verticalAlignment}
class={css?.container?.class ?? ''}
style={css?.container?.style ?? ''}
>
{#if iconComponent}
<svelte:component
this={iconComponent}
size={size || 24}
color={color || 'currentColor'}
strokeWidth={strokeWidth || 2}
class={css?.icon?.class ?? ''}
style={css?.icon?.style ?? ''}
/>
{/if}
</AlignWrapper>

View File

@@ -1,34 +1,41 @@
<script lang="ts">
import { getContext } from 'svelte'
import { twMerge } from 'tailwind-merge'
import type { staticValues } from '../../editor/componentsPanel/componentStaticValues'
import type { AppInput } from '../../inputType'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import { concatCustomCss } from '../../utils'
import InputValue from '../helpers/InputValue.svelte'
type FitOption = typeof staticValues['objectFitOptions'][number]
type FitOption = (typeof staticValues)['objectFitOptions'][number]
export let id: string
export let configuration: Record<string, AppInput>
export const staticOutputs: string[] = ['loading']
export let customCss: ComponentCustomCSS<'image'> | undefined = undefined
const { app } = getContext<AppEditorContext>('AppEditorContext')
const fit: Record<FitOption, string> = {
cover: 'object-cover',
contain: 'object-contain',
fill: 'object-fill',
fill: 'object-fill'
}
let source: string | undefined = undefined
let imageFit: FitOption | undefined = undefined
let altText: string | undefined = undefined
let customStyles: string | undefined = undefined
$: css = concatCustomCss($app.css?.imagecomponent, customCss)
</script>
<InputValue {id} input={configuration.source} bind:value={source} />
<InputValue {id} input={configuration.imageFit} bind:value={imageFit} />
<InputValue {id} input={configuration.altText} bind:value={altText} />
<InputValue {id} input={configuration.customStyles} bind:value={customStyles} />
<img
on:pointerdown|preventDefault
src={source}
alt={altText}
style={customStyles}
class="w-full h-full {fit[imageFit || 'cover']}"
>
style={css?.image?.style ?? ''}
class={twMerge(`w-full h-full ${fit[imageFit || 'cover']}`, css?.image?.class ?? '')}
/>

View File

@@ -0,0 +1,222 @@
<script lang="ts">
import { getContext, onMount } from 'svelte'
import { concatCustomCss } from '../../utils'
import type { AppInput } from '../../inputType'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import { InputValue } from '../helpers'
import { twMerge } from 'tailwind-merge'
import { Map, View, Feature } from 'ol'
import { useGeographic } from 'ol/proj'
import { OSM, Vector as VectorSource } from 'ol/source'
import { Vector as VectorLayer, Tile as TileLayer } from 'ol/layer'
import { Point } from 'ol/geom'
import { defaults as defaultControls } from 'ol/control'
import { findGridItem } from '../../editor/appUtils'
import type { Output } from '../../rx'
interface Marker {
lon: number
lat: number
title?: string
radius?: number
color?: string
strokeWidth?: number
strokeColor?: string
}
const LAYER_NAME = {
MARKER: 'Marker'
} as const
export let id: string
export let configuration: Record<string, AppInput>
export const staticOutputs: string[] = ['mapRegion']
export let customCss: ComponentCustomCSS<'map'> | undefined = undefined
const { app, worldStore, selectedComponent, connectingInput, focusedGrid, mode } =
getContext<AppEditorContext>('AppEditorContext')
$: outputs = $worldStore?.outputsById[id] as {
mapRegion: Output<{
topLeft: { lat: number; lon: number }
bottomRight: { lat: number; lon: number }
}>
}
let map: Map
let mapElement: HTMLDivElement
let longitude: number | undefined = undefined
let latitude: number | undefined = undefined
let zoom: number | undefined = undefined
// If string, it's a JSON file read as text
let markers: Marker[] | string | undefined = undefined
$: if (map && longitude && latitude) {
map.getView().setCenter([longitude, latitude])
}
$: if (map && zoom) {
map.getView().setZoom(zoom)
}
$: if (map && markers) {
updateMarkers()
}
function selectComponent() {
if (!$connectingInput.opened) {
$selectedComponent = id
$focusedGrid = undefined
}
}
function getLayersByName(name: keyof typeof LAYER_NAME) {
return map
.getLayers()
.getArray()
.filter((l) => l.getProperties().name === LAYER_NAME[name])
}
function getMarkerArray(): Marker[] | undefined {
let array: Marker[] | undefined = undefined
if (typeof markers === 'string') {
try {
array = JSON.parse(markers)
} catch (e) {
return undefined
}
} else {
array = markers
}
return array?.filter((m) => !isNaN(+m.lat) && !isNaN(+m.lon))
}
function createMarkerLayers() {
const markerArray = getMarkerArray()
return markerArray?.map((m) => {
return new VectorLayer({
properties: {
name: LAYER_NAME.MARKER
},
source: new VectorSource({
features: [
new Feature({
geometry: new Point([+m.lon, +m.lat]),
name: m.title
})
]
}),
style: {
'circle-radius': m.radius ?? 7,
'circle-fill-color': m.color ?? '#dc2626',
'circle-stroke-width': m.strokeWidth ?? 3,
'circle-stroke-color': m.strokeColor ?? '#fca5a5'
}
})
})
}
function updateMarkers() {
const layers = getLayersByName('MARKER')
if (layers?.length) {
layers.forEach((l) => map.removeLayer(l))
}
createMarkerLayers()?.forEach((l) => map.addLayer(l))
}
onMount(() => {
useGeographic()
map = new Map({
target: mapElement,
layers: [
new TileLayer({
source: new OSM()
}),
...(createMarkerLayers() || [])
],
view: new View({
center: [longitude ?? 0, latitude ?? 0],
zoom: zoom ?? 2
}),
controls: defaultControls({
attribution: false
})
})
updateRegionOutput()
})
$: css = concatCustomCss($app.css?.mapcomponent, customCss)
$: gridItem = findGridItem($app, id)
function updateRegionOutput() {
if (map) {
let extent = map.getView().calculateExtent(map.getSize())
const [left, bottom, right, top] = extent
if (outputs?.mapRegion) {
outputs.mapRegion.set({
topLeft: { lat: top, lon: left },
bottomRight: { lat: bottom, lon: right }
})
}
}
}
function handleSyncRegion() {
if (!map || !gridItem) {
return
}
const z = map.getView().getZoom()
updateRegionOutput()
if (z) {
gridItem.data.configuration.zoom.value = z
}
const center = map.getView().getCenter()
if (!center) {
return
}
if (gridItem) {
gridItem.data.configuration.longitude.value = center[0]
gridItem.data.configuration.latitude.value = center[1]
}
}
</script>
<InputValue {id} input={configuration.longitude} bind:value={longitude} />
<InputValue {id} input={configuration.latitude} bind:value={latitude} />
<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'}
<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>
<style global lang="postcss">
.ol-overlaycontainer-stopevent {
@apply flex flex-col justify-start items-end;
}
.ol-control button {
@apply w-7 h-7 center-center bg-white border border-gray-300 text-gray-700
rounded mt-1 mr-1 shadow duration-200 hover:bg-gray-100 focus:bg-gray-100
hover:border-gray-500 focus:border-gray-500;
}
</style>

View File

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

View File

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

View File

@@ -64,7 +64,7 @@
<InputValue {id} input={configuration.style} bind:value={style} />
<InputValue {id} input={configuration.copyButton} bind:value={copyButton} />
<RunnableWrapper flexWrap bind:componentInput {id} bind:initializing bind:result>
<RunnableWrapper flexWrap {componentInput} {id} bind:initializing bind:result>
<AlignWrapper {horizontalAlignment} {verticalAlignment}>
{#if !result || result === ''}
<div class="text-gray-400 bg-gray-100 flex justify-center items-center h-full w-full">

View File

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

View File

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

View File

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

View File

@@ -1,12 +1,14 @@
export { default as AppTable } from './table/AppTable.svelte'
export { default as AppAggridTable } from './table/AppAggridTable.svelte'
export { default as AppBarChart } from './AppBarChart.svelte'
export { default as AppDisplayComponent } from './AppDisplayComponent.svelte'
export { default as AppHtml } from './AppHtml.svelte'
export { default as AppIcon } from './AppIcon.svelte'
export { default as AppImage } from './AppImage.svelte'
export { default as AppMap } from './AppMap.svelte'
export { default as AppPieChart } from './AppPieChart.svelte'
export { default as AppScatterChart } from './AppScatterChart.svelte'
export { default as AppText } from './AppText.svelte'
export { default as AppTimeseries } from './AppTimeseries.svelte'
export { default as PlotlyHtml } from './PlotlyHtml.svelte'
export { default as VegaLiteHtml } from './VegaLiteHtml.svelte'
export { default as VegaLiteHtml } from './VegaLiteHtml.svelte'

View File

@@ -0,0 +1,118 @@
<script lang="ts">
import AgGridSvelte from 'ag-grid-svelte/AgGridSvelte.svelte'
import 'ag-grid-community/styles/ag-grid.css'
import 'ag-grid-community/styles/ag-theme-alpine.css'
import { getContext, onMount } from 'svelte'
import type { Output } from '../../../rx'
import type { AppEditorContext } from '../../../types'
import InputValue from '../../helpers/InputValue.svelte'
import type { AppInput } from '../../../inputType'
import RunnableWrapper from '../../helpers/RunnableWrapper.svelte'
import { isObject } from '$lib/utils'
import Alert from '$lib/components/common/alert/Alert.svelte'
export let id: string
export let componentInput: AppInput | undefined
export let configuration: Record<string, AppInput>
export let initializing: boolean | undefined = undefined
export const staticOutputs: string[] = ['selectedRow', 'loading', 'result', 'selectedRowIndex']
let result: Record<string, any>[] | undefined = undefined
const {
worldStore,
staticOutputs: staticOutputsStore,
selectedComponent
} = getContext<AppEditorContext>('AppEditorContext')
let selectedRowIndex = -1
function toggleRow(row: Record<string, any>, rowIndex: number) {
if (selectedRowIndex !== rowIndex) {
selectedRowIndex = rowIndex
outputs?.selectedRow.set(row.original)
outputs?.selectedRowIndex.set(rowIndex)
}
}
let mounted = false
onMount(() => {
mounted = true
})
$: selectedRowIndex === -1 &&
Array.isArray(result) &&
result.length > 0 &&
// We need to wait until the component is mounted so the world is created
mounted &&
outputs &&
toggleRow({ original: result[0] }, 0)
$: outputs = $worldStore?.outputsById[id] as {
selectedRowIndex: Output<number>
selectedRow: Output<any>
result: Output<any>
}
$: outputs?.result?.set(result)
let clientHeight
let clientWidth
let columnDefs: any = undefined
let allEditable: boolean | undefined = undefined
let pagination: boolean | undefined = undefined
let pageSize: number | undefined = 10
function onCellValueChanged(event) {
if (result) {
let dataCell = event.newValue
try {
dataCell = JSON.parse(dataCell)
} catch (e) {}
result[event.node.rowIndex][event.column.colId] = dataCell
}
}
</script>
<InputValue {id} input={configuration.columnDefs} bind:value={columnDefs} />
<InputValue {id} input={configuration.allEditable} bind:value={allEditable} />
<InputValue {id} input={configuration.pagination} bind:value={pagination} />
<InputValue {id} input={configuration.pageSize} bind:value={pageSize} />
<RunnableWrapper 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"
bind:clientHeight
bind:clientWidth
>
<div
on:pointerdown|stopPropagation={() => {
$selectedComponent = id
}}
style:height="{clientHeight}px"
style:width="{clientWidth}px"
class="ag-theme-alpine"
>
{#key pagination}
<AgGridSvelte
bind:rowData={result}
{columnDefs}
{pagination}
paginationPageSize={pageSize}
defaultColDef={{ flex: 1, editable: allEditable, onCellValueChanged }}
/>
{/key}
</div>
</div>
{:else if result != undefined}
<Alert title="Parsing issues" type="error" size="xs">
The result should be an array of objects
</Alert>
{/if}
</RunnableWrapper>

View File

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

View File

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

View File

@@ -1,41 +1,44 @@
<script lang="ts">
import { classNames } from '$lib/utils'
import { twMerge } from 'tailwind-merge'
import type { HorizontalAlignment, VerticalAlignment } from '../../types'
export let horizontalAlignment: HorizontalAlignment | undefined = undefined
export let verticalAlignment: VerticalAlignment | undefined = undefined
export let noWFull = false
let c = ''
export { c as class }
export let style = ''
function tailwindHorizontalAlignment(alignment?: HorizontalAlignment) {
if(!alignment) return '';
if (!alignment) return ''
const classes: Record<HorizontalAlignment, string> = {
left: 'justify-start',
center: 'justify-center',
right: 'justify-end',
right: 'justify-end'
}
return classes[alignment]
}
function tailwindVerticalAlignment(alignment?: VerticalAlignment) {
if(!alignment) return '';
if (!alignment) return ''
const classes: Record<VerticalAlignment, string> = {
top: 'items-start',
center: 'items-center',
bottom: 'items-end',
bottom: 'items-end'
}
return classes[alignment]
}
$: classes = classNames(
$: classes = twMerge(
'flex z-auto',
noWFull ? '' : 'w-full',
tailwindHorizontalAlignment(horizontalAlignment),
tailwindVerticalAlignment(verticalAlignment),
verticalAlignment ? 'h-full' : '',
$$props.class || ''
c
)
</script>
<div class={classes}>
<div class={classes} {style}>
<slot />
</div>

View File

@@ -1,5 +1,10 @@
<script lang="ts">
import type { ConnectedAppInput, RowAppInput, StaticAppInput, UserAppInput } from '../../inputType'
import type {
ConnectedAppInput,
RowAppInput,
StaticAppInput,
UserAppInput
} from '../../inputType'
import type { InlineScript } from '../../types'
import RunnableComponent from './RunnableComponent.svelte'
@@ -15,7 +20,7 @@
<RunnableComponent
{id}
bind:fields
{fields}
bind:result
runnable={{
name,

View File

@@ -1,5 +1,6 @@
<script lang="ts">
import { isCodeInjection } from '$lib/components/flows/utils'
import { deepEqual } from 'fast-equals'
import { getContext } from 'svelte'
import type { AppInput, EvalAppInput, UploadAppInput } from '../../inputType'
import type { AppEditorContext } from '../../types'
@@ -10,29 +11,34 @@
export let input: AppInput
export let value: T
export let id: string | undefined = undefined
export let row: Record<string, any> | undefined = {}
export let error: string = ''
let lastInput = input ? JSON.parse(JSON.stringify(input)) : undefined
$: if (input && !deepEqual(input, lastInput)) {
lastInput = JSON.parse(JSON.stringify(input))
}
const { worldStore } = getContext<AppEditorContext>('AppEditorContext')
$: state = $worldStore?.state
$: input && $worldStore && row && handleConnection()
$: input && input.type == 'template' && $state && (value = getValue(input))
$: input && input.type == 'eval' && $state && (value = evalExpr(input))
$: lastInput && $worldStore && handleConnection()
$: lastInput && lastInput.type == 'template' && $state && (value = getValue(lastInput))
$: lastInput && lastInput.type == 'eval' && $state && (value = evalExpr(lastInput))
function handleConnection() {
if (input.type === 'connected') {
$worldStore?.connect<any>(input, onValueChange)
} else if (input.type === 'row') {
setTimeout(() => (value = row?.[input['column']]), 0)
} else if (input.type === 'static' || input.type == 'template') {
setTimeout(() => (value = getValue(input)), 0)
} else if (input.type == 'eval') {
setTimeout(() => ((value = evalExpr(input as EvalAppInput)), 0))
} else if (input.type == 'upload') {
setTimeout(() => ((value = (input as UploadAppInput).value), 0))
// console.log('handle connection', id, lastInput)
if (lastInput.type === 'connected') {
$worldStore?.connect<any>(lastInput, onValueChange)
} else if (lastInput.type === 'static' || lastInput.type == 'template') {
value = getValue(lastInput)
} else if (lastInput.type == 'eval') {
value = evalExpr(lastInput as EvalAppInput)
} else if (lastInput.type == 'upload') {
value = (lastInput as UploadAppInput).value
} else {
setTimeout(() => (value = undefined), 0)
value = undefined
}
}
@@ -57,7 +63,6 @@
Object.fromEntries(Object.entries(value ?? {}).map((x) => [x[0], x[1].peak()]))
]
})
.concat(row ? [['row', row]] : [])
)
}
@@ -103,8 +108,8 @@
}
function onValueChange(newValue: any): void {
if (input.type === 'connected' && newValue !== undefined && newValue !== null) {
const { connection } = input
if (lastInput.type === 'connected' && newValue !== undefined && newValue !== null) {
const { connection } = lastInput
if (!connection) {
// No connection

View File

@@ -7,7 +7,6 @@
import { AppService, type CompletedJob } from '$lib/gen'
import { defaultIfEmptyString, emptySchema, sendUserToast } from '$lib/utils'
import { getContext, onMount } from 'svelte'
import { computeFields } from '../../editor/inlineScriptsPanel/utils'
import type { AppInputs, Runnable } from '../../inputType'
import type { Output } from '../../rx'
import type { AppEditorContext } from '../../types'
@@ -22,11 +21,12 @@
export let autoRefresh: boolean = true
export let result: any = undefined
export let forceSchemaDisplay: boolean = false
export let defaultUserInput = false
export let flexWrap = false
export let wrapperClass = ''
export let wrapperStyle = ''
export let initializing: boolean | undefined = undefined
export let gotoUrl: string | undefined = undefined
export let gotoNewTab: boolean | undefined = undefined
const {
worldStore,
@@ -37,7 +37,8 @@
jobs,
noBackend,
errorByComponent,
mode
mode,
stateId
} = getContext<AppEditorContext>('AppEditorContext')
onMount(() => {
@@ -104,29 +105,9 @@
$: outputs?.loading?.set(testIsLoading)
$: runnable?.type === 'runnableByName' && loadSchemaAndInputsByName()
$: schemaStripped = stripSchema(fields, $stateId)
async function loadSchemaAndInputsByName() {
if (runnable?.type === 'runnableByName') {
const { inlineScript } = runnable
// Inline scripts directly provide the schema
if (inlineScript) {
const newSchema = inlineScript.schema
const newFields = computeFields(newSchema, defaultUserInput, fields)
if (JSON.stringify(newFields) !== JSON.stringify(fields)) {
setTimeout(() => {
fields = newFields
}, 0)
}
}
}
}
$: schemaStripped = runnable && stripSchema(fields)
function stripSchema(inputs: AppInputs): Schema {
function stripSchema(inputs: AppInputs, s: any): Schema {
let schema =
runnable?.type == 'runnableByName' ? runnable.inlineScript?.schema : runnable?.schema
try {
@@ -239,12 +220,7 @@
{#each Object.entries(fields ?? {}) as [key, v]}
{#if v.type != 'static' && v.type != 'user'}
<InputValue
{id}
input={fields[key]}
bind:value={runnableInputValues[key]}
row={extraQueryParams['row'] ?? {}}
/>
<InputValue {id} input={fields[key]} bind:value={runnableInputValues[key]} />
{/if}
{/each}
@@ -266,7 +242,13 @@
delete $errorByComponent[previousJobId]
$errorByComponent = $errorByComponent
}
gotoUrl && gotoUrl != '' && result?.error == undefined && goto(gotoUrl)
if (gotoUrl && gotoUrl != '' && result?.error == undefined) {
if (gotoNewTab) {
window.open(gotoUrl, '_blank')
} else {
goto(gotoUrl)
}
}
}
}
}}
@@ -275,12 +257,7 @@
bind:this={testJobLoader}
/>
<div class="h-full flex relative flex-row flex-wrap {wrapperClass}">
{#if !initializing && autoRefresh === true}
<div class="flex absolute top-1 right-1">
<RefreshButton componentId={id} />
</div>
{/if}
<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
@@ -316,4 +293,9 @@
<slot />
</div>
{/if}
{#if !initializing && autoRefresh === true}
<div class="flex absolute top-1 right-1 z-50">
<RefreshButton componentId={id} />
</div>
{/if}
</div>

View File

@@ -15,10 +15,11 @@
export let autoRefresh: boolean = true
export let runnableComponent: RunnableComponent | undefined = undefined
export let forceSchemaDisplay: boolean = false
export let defaultUserInput = false
export let flexWrap = false
export let runnableClass = ''
export let runnableStyle = ''
export let goto: string | undefined = undefined
export let gotoNewTab: boolean | undefined = undefined
const { staticExporter, noBackend } = getContext<AppEditorContext>('AppEditorContext')
@@ -43,10 +44,10 @@
{:else if componentInput.type === 'runnable' && isRunnableDefined()}
<RunnableComponent
gotoUrl={goto}
{gotoNewTab}
{flexWrap}
{defaultUserInput}
bind:this={runnableComponent}
bind:fields={componentInput.fields}
fields={componentInput.fields}
bind:result
runnable={componentInput.runnable}
{autoRefresh}
@@ -55,6 +56,7 @@
{forceSchemaDisplay}
{initializing}
wrapperClass={runnableClass}
wrapperStyle={runnableStyle}
>
<slot />
</RunnableComponent>

View File

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

View File

@@ -1,8 +1,10 @@
<script lang="ts">
import { getContext } from 'svelte'
import { twMerge } from 'tailwind-merge'
import type { AppInput } from '../../inputType'
import type { Output } from '../../rx'
import type { AppEditorContext } from '../../types'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import { concatCustomCss } from '../../utils'
import AlignWrapper from '../helpers/AlignWrapper.svelte'
import InputDefaultValue from '../helpers/InputDefaultValue.svelte'
import InputValue from '../helpers/InputValue.svelte'
@@ -12,8 +14,9 @@
export let inputType: 'date' | 'time' | 'datetime-local'
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
export const staticOutputs: string[] = ['result']
export let customCss: ComponentCustomCSS<'input'> | undefined = undefined
const { worldStore } = getContext<AppEditorContext>('AppEditorContext')
const { app, worldStore } = getContext<AppEditorContext>('AppEditorContext')
let input: HTMLInputElement
let labelValue: string = 'Title'
let minValue: string = ''
@@ -29,6 +32,8 @@
}
$: input && handleInput()
$: css = concatCustomCss($app.css?.dateinputcomponent, customCss)
</script>
<InputValue {id} input={configuration.label} bind:value={labelValue} />
@@ -46,6 +51,7 @@
min={minValue}
max={maxValue}
placeholder="Type..."
class="h-full"
class={twMerge('mx-0.5', css?.input?.class ?? '')}
style={css?.input?.style ?? ''}
/>
</AlignWrapper>

View File

@@ -1,16 +1,19 @@
<script lang="ts">
import { getContext } from 'svelte'
import { twMerge } from 'tailwind-merge'
import { FileInput } from '../../../common'
import type { AppInput } from '../../inputType'
import type { Output } from '../../rx'
import type { AppEditorContext } from '../../types'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import { concatCustomCss } from '../../utils'
import InputValue from '../helpers/InputValue.svelte'
export let id: string
export let configuration: Record<string, AppInput>
export const staticOutputs: string[] = ['result']
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
const { worldStore } = getContext<AppEditorContext>('AppEditorContext')
const { app, worldStore } = getContext<AppEditorContext>('AppEditorContext')
let acceptedFileTypes: string[] | undefined = undefined
let allowMultiple: boolean | undefined = undefined
@@ -24,6 +27,8 @@
async function handleChange(files: string[] | undefined) {
outputs?.result.set(files)
}
$: css = concatCustomCss($app.css?.fileinputcomponent, customCss)
</script>
<InputValue {id} input={configuration.acceptedFileTypes} bind:value={acceptedFileTypes} />
@@ -34,11 +39,12 @@
<FileInput
accept={acceptedFileTypes?.length ? acceptedFileTypes?.join(', ') : undefined}
multiple={allowMultiple}
convertToBase64
convertTo="base64"
on:change={({ detail }) => {
handleChange(detail)
}}
class="w-full h-full"
class={twMerge('w-full h-full', css?.container?.class)}
style={css?.container?.style}
>
{text}
</FileInput>

View File

@@ -0,0 +1,97 @@
<script lang="ts">
import { getContext } from 'svelte'
import Select from 'svelte-select'
import type { AppInput } from '../../inputType'
import type { Output } from '../../rx'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import { concatCustomCss } from '../../utils'
import AlignWrapper from '../helpers/AlignWrapper.svelte'
import InputValue from '../helpers/InputValue.svelte'
import { SELECT_INPUT_DEFAULT_STYLE } from '../../../../defaults'
import { stopPropagation } from 'ol/events/Event'
export const staticOutputs: string[] = ['result']
export let id: string
export let configuration: Record<string, AppInput>
export let horizontalAlignment: 'left' | 'center' | 'right' | undefined = undefined
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
export let customCss: ComponentCustomCSS<'input'> | undefined = undefined
const { app, worldStore, connectingInput, selectedComponent } =
getContext<AppEditorContext>('AppEditorContext')
let items: { label: string; value: string }[]
let placeholder: string = 'Select an item'
$: outputs = $worldStore?.outputsById[id] as {
result: Output<string[] | undefined>
}
// $: outputs && handleOutputs()
// function handleOutputs() {
// value = outputs.result.peak()
// }
let value: { value: string }[] | undefined = outputs?.result.peak()
$: labels && handleItems()
let labels: string[] | undefined = []
function handleItems() {
if (labels) {
items = labels?.map((label) => {
return {
label,
value: label
}
})
}
}
$: value && outputs?.result.set(value.map((v) => v.value))
$: css = concatCustomCss($app.css?.selectcomponent, customCss)
</script>
<InputValue {id} input={configuration.items} bind:value={labels} />
<InputValue {id} input={configuration.placeholder} bind:value={placeholder} />
<AlignWrapper {horizontalAlignment} {verticalAlignment}>
<div class="app-select w-full mx-0.5" style="height: 34px" on:pointerdown|stopPropagation>
{#if !value || Array.isArray(value)}
<Select
multiple
on:change={(e) => e.stopPropagation()}
{items}
class={css?.input?.class ?? ''}
inputStyles={SELECT_INPUT_DEFAULT_STYLE.inputStyles}
containerStyles={SELECT_INPUT_DEFAULT_STYLE.containerStyles + css?.input?.style}
bind:value
{placeholder}
on:click={() => {
if (!$connectingInput.opened) {
$selectedComponent = id
}
}}
on:focus={() => {
$selectedComponent = id
}}
floatingConfig={{
strategy: 'fixed'
}}
/>
{:else}
Value {value} is not an array
{/if}
</div>
</AlignWrapper>
<style global>
.app-select .value-container {
padding: 0 !important;
}
.svelte-select-list {
z-index: 1000 !important;
}
</style>

View File

@@ -2,7 +2,8 @@
import { getContext } from 'svelte'
import type { AppInput } from '../../inputType'
import type { Output } from '../../rx'
import type { AppEditorContext } from '../../types'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import { concatCustomCss } from '../../utils'
import AlignWrapper from '../helpers/AlignWrapper.svelte'
import InputDefaultValue from '../helpers/InputDefaultValue.svelte'
import InputValue from '../helpers/InputValue.svelte'
@@ -11,8 +12,9 @@
export let configuration: Record<string, AppInput>
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
export const staticOutputs: string[] = ['result']
export let customCss: ComponentCustomCSS<'input'> | undefined = undefined
const { worldStore } = getContext<AppEditorContext>('AppEditorContext')
const { app, worldStore } = getContext<AppEditorContext>('AppEditorContext')
let input: HTMLInputElement
let defaultValue: number | undefined = undefined
@@ -33,6 +35,8 @@
}
$: input && handleInput()
$: css = concatCustomCss($app.css?.numberinputcomponent, customCss)
</script>
<InputValue {id} input={configuration.step} bind:value={step} />
@@ -44,6 +48,8 @@
<AlignWrapper {verticalAlignment}>
<input
class="mx-0.5 {css?.input?.class ?? ''}"
style={css?.input?.style ?? ''}
bind:this={input}
on:input={handleInput}
on:focus={(e) => {

View File

@@ -3,20 +3,24 @@
import { getContext } from 'svelte'
import type { AppInput } from '../../inputType'
import type { Output } from '../../rx'
import type { AppEditorContext } from '../../types'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import AlignWrapper from '../helpers/AlignWrapper.svelte'
import InputValue from '../helpers/InputValue.svelte'
import { Badge } from '$lib/components/common'
import { concatCustomCss } from '../../utils'
import { twMerge } from 'tailwind-merge'
export let id: string
export let configuration: Record<string, AppInput>
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
export const staticOutputs: string[] = ['result']
export let customCss: ComponentCustomCSS<'handles' | 'bar' | 'limits' | 'values'> | undefined =
undefined
const { worldStore } = getContext<AppEditorContext>('AppEditorContext')
const { app, worldStore } = getContext<AppEditorContext>('AppEditorContext')
let min = 0
let max = 42
let step = 1
let slider: HTMLElement
let values: [number, number] = [0, 0]
@@ -25,6 +29,17 @@
}
$: outputs?.result.set(values)
$: css = concatCustomCss($app.css?.rangecomponent, customCss)
let lastStyle: string | undefined = undefined
$: if (css && slider && lastStyle !== css?.handles?.style) {
const handles = slider.querySelectorAll<HTMLSpanElement>('.rangeNub')
if (handles) {
lastStyle = css?.handles?.style
handles.forEach((handle) => (handle.style.cssText = css?.handles?.style ?? ''))
}
}
</script>
<InputValue {id} input={configuration.step} bind:value={step} />
@@ -36,20 +51,43 @@
<AlignWrapper {verticalAlignment}>
<div class="flex flex-col w-full">
<div class="flex items-center w-full gap-1 px-1">
<span>{min}</span>
<span class={css?.limits?.class ?? ''} style={css?.limits?.style ?? ''}>
{+min}
</span>
<div class="grow" on:pointerdown|stopPropagation>
<RangeSlider {step} range min={min ?? 0} max={max ?? 1} bind:values />
<!-- <DoubleRange bind:start bind:end /> -->
<RangeSlider
bind:slider
bind:values
{step}
min={!min ? 0 : +min}
max={!max ? 1 : +max}
range
/>
<!-- <RangeSlider {step} range min={min ?? 0} max={max ?? 1} bind:values /> -->
</div>
<span>{max}</span>
<span class={css?.limits?.class ?? ''} style={css?.limits?.style ?? ''}>
{+max}
</span>
</div>
<div class="flex justify-between px-1">
<div>
<Badge color="blue">{values[0]}</Badge>
</div>
<div>
<Badge color="blue">{values[1]}</Badge>
</div>
<span
class={twMerge(
'text-center text-sm font-medium bg-blue-100 text-blue-800 rounded px-2.5 py-0.5',
css?.values?.class ?? ''
)}
style={css?.values?.style ?? ''}
>
{values[0]}
</span>
<span
class={twMerge(
'text-center text-sm font-medium bg-blue-100 text-blue-800 rounded px-2.5 py-0.5',
css?.values?.class ?? ''
)}
style={css?.values?.style ?? ''}
>
{values[1]}
</span>
</div>
</div>
</AlignWrapper>

View File

@@ -3,31 +3,33 @@
import Select from 'svelte-select'
import type { AppInput } from '../../inputType'
import type { Output } from '../../rx'
import type { AppEditorContext } from '../../types'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import { concatCustomCss } from '../../utils'
import AlignWrapper from '../helpers/AlignWrapper.svelte'
import InputValue from '../helpers/InputValue.svelte'
import { SELECT_INPUT_DEFAULT_STYLE } from '../../../../defaults'
export const staticOutputs: string[] = ['result']
export let id: string
export let configuration: Record<string, AppInput>
export let horizontalAlignment: 'left' | 'center' | 'right' | undefined = undefined
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
export let customCss: ComponentCustomCSS<'input'> | undefined = undefined
const { worldStore, connectingInput, selectedComponent } =
const { app, worldStore, connectingInput, selectedComponent } =
getContext<AppEditorContext>('AppEditorContext')
let items: { label: string; value: any }[]
let multiple: boolean = false
let items: { label: string; value: any; created?: boolean }[]
let placeholder: string = 'Select an item'
$: outputs = $worldStore?.outputsById[id] as {
result: Output<string | undefined>
result: Output<any | undefined>
}
let value: string | undefined = undefined
let value: string | undefined = outputs?.result.peak()
$: items && handleItems()
let listItems: { label: string; value: string }[] = []
let listItems: { label: string; value: string; created?: boolean }[] = []
function handleItems() {
listItems = Array.isArray(items)
@@ -38,35 +40,73 @@
}
})
: []
if (items?.[0]?.['value'] != value) {
value = JSON.stringify(items?.[0]?.['value'])
outputs?.result.set(value)
if (defaultValue) {
value = JSON.stringify(defaultValue)
}
}
function onChange(e: CustomEvent) {
e?.stopPropagation()
window.dispatchEvent(new Event('pointerup'))
if (create) {
listItems = listItems.map((i) => {
delete i.created
return i
})
}
let result: any = undefined
try {
result = JSON.parse(e.detail?.['value'])
} catch (_) {}
outputs?.result.set(result)
}
$: css = concatCustomCss($app.css?.selectcomponent, customCss)
let defaultValue: any = undefined
function handleFilter(e) {
if (create) {
if (e.detail.length === 0 && filterText.length > 0) {
const prev = listItems.filter((i) => !i.created)
listItems = [
...prev,
{ value: JSON.stringify(filterText), label: filterText, created: true }
]
}
}
}
$: defaultValue && handleDefault()
function handleDefault() {
if (defaultValue) {
value = JSON.stringify(defaultValue)
outputs?.result.set(defaultValue)
}
}
let create = false
let filterText = ''
</script>
<InputValue {id} input={configuration.items} bind:value={items} />
<InputValue {id} input={configuration.multiple} bind:value={multiple} />
<InputValue {id} input={configuration.placeholder} bind:value={placeholder} />
<InputValue {id} input={configuration.defaultValue} bind:value={defaultValue} />
<InputValue {id} input={configuration.create} bind:value={create} />
<AlignWrapper {horizontalAlignment} {verticalAlignment}>
<div class="app-select w-full" style="height: 34px" on:pointerdown|stopPropagation>
<div class="app-select w-full mx-0.5" style="height: 34px" on:pointerdown|stopPropagation>
<Select
--height="34px"
{multiple}
bind:filterText
on:filter={handleFilter}
on:clear={onChange}
on:change={onChange}
items={listItems}
class={css?.input?.class ?? ''}
inputStyles={SELECT_INPUT_DEFAULT_STYLE.inputStyles}
containerStyles={SELECT_INPUT_DEFAULT_STYLE.containerStyles + css?.input?.style}
{value}
{placeholder}
on:click={() => {
@@ -74,7 +114,21 @@
$selectedComponent = id
}
}}
/>
on:focus={() => {
$selectedComponent = id
}}
floatingConfig={{
strategy: 'fixed'
}}
>
<div slot="item" let:item>
{#if create}
{item.created ? 'Add new: ' : ''}
{/if}
{item.label}
</div>
</Select>
</div>
</AlignWrapper>
@@ -82,4 +136,7 @@
.app-select .value-container {
padding: 0 !important;
}
.svelte-select-list {
z-index: 1000 !important;
}
</style>

View File

@@ -1,28 +1,35 @@
<script lang="ts">
import { Badge } from '$lib/components/common'
import RangeSlider from 'svelte-range-slider-pips'
import { getContext } from 'svelte'
import type { AppInput } from '../../inputType'
import type { Output } from '../../rx'
import type { AppEditorContext } from '../../types'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import AlignWrapper from '../helpers/AlignWrapper.svelte'
import InputValue from '../helpers/InputValue.svelte'
import { twMerge } from 'tailwind-merge'
import { concatCustomCss } from '../../utils'
export let id: string
export let configuration: Record<string, AppInput>
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
export const staticOutputs: string[] = ['result']
export let customCss: ComponentCustomCSS<'handle' | 'limits' | 'value'> | undefined = undefined
const { worldStore } = getContext<AppEditorContext>('AppEditorContext')
let values: [number] = [0]
const { app, worldStore, selectedComponent } = getContext<AppEditorContext>('AppEditorContext')
let min = 0
let max = 42
let step = 1
let slider: HTMLElement
$: outputs = $worldStore?.outputsById[id] as {
result: Output<number | null>
}
$: if (values) {
let values: [number] = [outputs?.result.peak() ?? 0]
$: values && handleValues()
function handleValues() {
// Disallow 'e' character in numbers
// if(value && value.toString().includes('e')) {
// value = +value.toString().replaceAll('e', '')
@@ -30,6 +37,17 @@
const num = isNaN(+values[0]) ? null : +values[0]
outputs?.result.set(num)
}
$: css = concatCustomCss($app.css?.slidercomponent, customCss)
let lastStyle: string | undefined = undefined
$: if (css && slider && lastStyle !== css?.handle?.style) {
const handle = slider.querySelector<HTMLSpanElement>('.rangeNub')
if (handle) {
lastStyle = css?.handle?.style
handle.style.cssText = css?.handle?.style ?? ''
}
}
</script>
<InputValue {id} input={configuration.step} bind:value={step} />
@@ -39,11 +57,25 @@
<AlignWrapper {verticalAlignment}>
<div class="flex items-center w-full gap-1 px-1">
<span>{min}</span>
<div class="grow" on:pointerdown|stopPropagation>
<RangeSlider {step} min={min ?? 0} max={max ?? 1} bind:values />
<span class={css?.limits?.class ?? ''} style={css?.limits?.style ?? ''}>
{+min}
</span>
<div class="grow" on:pointerdown|stopPropagation={() => ($selectedComponent = id)}>
<RangeSlider bind:slider bind:values {step} min={+min} max={+max} />
</div>
<span>{max}</span>
<span class="mx-2"><Badge large color="blue">{values[0]}</Badge></span>
<span class={css?.limits?.class ?? ''} style={css?.limits?.style ?? ''}>
{+max}
</span>
<span class="mx-2">
<span
class={twMerge(
'text-center text-sm font-medium bg-blue-100 text-blue-800 rounded px-2.5 py-0.5',
css?.value?.class ?? ''
)}
style={css?.value?.style ?? ''}
>
{values[0]}
</span>
</span>
</div>
</AlignWrapper>

View File

@@ -1,8 +1,10 @@
<script lang="ts">
import { getContext } from 'svelte'
import { twMerge } from 'tailwind-merge'
import type { AppInput } from '../../inputType'
import type { Output } from '../../rx'
import type { AppEditorContext } from '../../types'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import { concatCustomCss } from '../../utils'
import AlignWrapper from '../helpers/AlignWrapper.svelte'
import InputDefaultValue from '../helpers/InputDefaultValue.svelte'
import InputValue from '../helpers/InputValue.svelte'
@@ -12,8 +14,10 @@
export let inputType = 'text'
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'
const { worldStore } = getContext<AppEditorContext>('AppEditorContext')
const { app, worldStore } = getContext<AppEditorContext>('AppEditorContext')
let input: HTMLInputElement
let placeholder: string | undefined = undefined
@@ -28,6 +32,8 @@
}
$: input && handleInput()
$: css = concatCustomCss($app.css?.[appCssKey], customCss)
</script>
<InputValue {id} input={configuration.placeholder} bind:value={placeholder} />
@@ -36,7 +42,8 @@
<AlignWrapper {verticalAlignment}>
<input
class="mx-0.5"
class={twMerge('mx-0.5', css?.input?.class ?? '')}
style={css?.input?.style ?? ''}
on:focus={(e) => {
e?.stopPropagation()
window.dispatchEvent(new Event('pointerup'))

View File

@@ -2,7 +2,8 @@
import { getContext } from 'svelte'
import type { AppInput } from '../../../inputType'
import type { Output } from '../../../rx'
import type { AppEditorContext } from '../../../types'
import type { AppEditorContext, ComponentCustomCSS } from '../../../types'
import { concatCustomCss } from '../../../utils'
import AlignWrapper from '../../helpers/AlignWrapper.svelte'
import InputValue from '../../helpers/InputValue.svelte'
import CurrencyInput from './CurrencyInput.svelte'
@@ -11,8 +12,9 @@
export let configuration: Record<string, AppInput>
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
export const staticOutputs: string[] = ['result']
export let customCss: ComponentCustomCSS<'input'> | undefined = undefined
const { worldStore } = getContext<AppEditorContext>('AppEditorContext')
const { app, worldStore } = getContext<AppEditorContext>('AppEditorContext')
let defaultValue: number | undefined = undefined
@@ -37,6 +39,8 @@
$: value != undefined && handleInput()
$: defaultValue != undefined && handleDefault()
$: css = concatCustomCss($app.css?.currencycomponent, customCss)
</script>
<InputValue {id} input={configuration.defaultValue} bind:value={defaultValue} />
@@ -49,7 +53,12 @@
{#key locale}
{#key currency}
<CurrencyInput
inputClasses={{ formatted: 'p-0', wrapper: 'w-full', formattedZero: 'text-black' }}
inputClasses={{
formatted: 'p-0 ' + css?.input?.class,
wrapper: 'w-full',
formattedZero: 'text-black ' + css?.input?.class
}}
style={css?.input?.style}
bind:value
{currency}
{locale}

View File

@@ -35,6 +35,7 @@
export let isNegativeAllowed: boolean = true
export let fractionDigits: number = DEFAULT_FRACTION_DIGITS
export let inputClasses: InputClasses | null = null
export let style: string = ''
// Formats value as: e.g. $1,523.00 | -$1,523.00
const formatCurrency = (
@@ -175,6 +176,7 @@
? inputClasses?.formattedNegative ?? DEFAULT_CLASS_FORMATTED_NEGATIVE
: ''}
"
{style}
type="text"
inputmode="numeric"
name={`formatted-${name}`}

View File

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

View File

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

View File

@@ -1,6 +1,14 @@
<script lang="ts">
import { getContext } from 'svelte'
import { twMerge } from 'tailwind-merge'
import type { AppInput } from '../../inputType'
import type { HorizontalAlignment, VerticalAlignment } from '../../types'
import type {
AppEditorContext,
ComponentCustomCSS,
HorizontalAlignment,
VerticalAlignment
} from '../../types'
import { concatCustomCss } from '../../utils'
import AlignWrapper from '../helpers/AlignWrapper.svelte'
import InputValue from '../helpers/InputValue.svelte'
@@ -8,17 +16,34 @@
export let configuration: Record<string, AppInput>
export let horizontalAlignment: HorizontalAlignment | undefined = undefined
export let verticalAlignment: VerticalAlignment | undefined = undefined
export let customCss: ComponentCustomCSS<'container' | 'divider'> | undefined = undefined
export let position: 'horizontal' | 'vertical'
const { app } = getContext<AppEditorContext>('AppEditorContext')
let size = 2
let color = '#00000060'
$: css = concatCustomCss($app.css?.[position + 'dividercomponent'], customCss)
</script>
<InputValue {id} input={configuration.size} bind:value={size} />
<InputValue {id} input={configuration.color} bind:value={color} />
<AlignWrapper {horizontalAlignment} {verticalAlignment} class="h-full">
<AlignWrapper
{horizontalAlignment}
{verticalAlignment}
class={twMerge(css?.container?.class, 'h-full')}
style={css?.container?.style}
>
<div
class="rounded-full {position === 'horizontal' ? 'w-full' : 'h-full'}"
style="{position === 'horizontal' ? 'height' : 'width'}: {size}px; background-color: {color}"
class={twMerge(
`rounded-full ${position === 'horizontal' ? 'w-full' : 'h-full'}`,
css?.divider?.class ?? ''
)}
style="
{position === 'horizontal' ? 'height' : 'width'}: {size}px;
background-color: {color};
{css?.divider?.style ?? ''}
"
/>
</AlignWrapper>

View File

@@ -0,0 +1,95 @@
<script lang="ts">
import { getContext } from 'svelte'
import SubGridEditor from '../../editor/SubGridEditor.svelte'
import type { AppInput } from '../../inputType'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import InputValue from '../helpers/InputValue.svelte'
import Portal from 'svelte-portal'
import { concatCustomCss } from '../../utils'
import { Button, ButtonType, Drawer, DrawerContent } from '$lib/components/common'
import { twMerge } from 'tailwind-merge'
import { AlignWrapper } from '../helpers'
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
export let id: string
export let configuration: Record<string, AppInput>
export let horizontalAlignment: 'left' | 'center' | 'right' | undefined = undefined
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
export let noWFull = false
const { app, focusedGrid, selectedComponent } = getContext<AppEditorContext>('AppEditorContext')
let gridContent: string[] | undefined = undefined
let noPadding: boolean | undefined = undefined
let drawerTitle: string | undefined = undefined
let appDrawer: Drawer
let labelValue: string
let color: ButtonType.Color
let size: ButtonType.Size
let disabled: boolean | undefined = undefined
let fillContainer: boolean | undefined = undefined
$: css = concatCustomCss($app.css?.containercomponent, customCss)
</script>
<div class="h-full w-full">
<AlignWrapper {noWFull} {horizontalAlignment} {verticalAlignment}>
<Button
btnClasses={twMerge(
$app.css?.['buttoncomponent']?.['button']?.class,
fillContainer ? 'w-full h-full' : ''
)}
{disabled}
on:pointerdown={(e) => {
e?.stopPropagation()
window.dispatchEvent(new Event('pointerup'))
}}
on:click={async (e) => {
$focusedGrid = {
parentComponentId: id,
subGridIndex: 0
}
appDrawer.toggleDrawer()
}}
{size}
{color}
>
<div>{labelValue}</div>
</Button>
</AlignWrapper>
</div>
<InputValue {id} input={configuration.gridContent} bind:value={gridContent} />
<InputValue {id} input={configuration.noPadding} bind:value={noPadding} />
<InputValue {id} input={configuration.drawerTitle} bind:value={drawerTitle} />
<InputValue {id} input={configuration.label} bind:value={labelValue} />
<InputValue {id} input={configuration.color} bind:value={color} />
<InputValue {id} input={configuration.size} bind:value={size} />
<InputValue {id} input={configuration.fillContainer} bind:value={fillContainer} />
<Portal target="#app-editor-top-level-drawer">
<Drawer bind:this={appDrawer} size="800px" alwaysOpen positionClass="!absolute">
<DrawerContent
title={drawerTitle}
on:close={() => {
appDrawer?.toggleDrawer()
$focusedGrid = undefined
}}
>
{#if $app.subgrids?.[`${id}-0`]}
<SubGridEditor
{noPadding}
{id}
class={css?.container.class}
style={css?.container.style}
bind:subGrid={$app.subgrids[`${id}-0`]}
containerHeight={1200}
on:focus={() => {
$selectedComponent = id
}}
/>
{/if}
</DrawerContent>
</Drawer>
</Portal>

View File

@@ -0,0 +1,73 @@
<script lang="ts">
import { getContext } from 'svelte'
import SubGridEditor from '../../editor/SubGridEditor.svelte'
import type { AppInput } from '../../inputType'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import { concatCustomCss } from '../../utils'
import InputValue from '../helpers/InputValue.svelte'
import { Pane, Splitpanes } from 'svelte-splitpanes'
import { deepEqual } from 'fast-equals'
export let id: string
export let configuration: Record<string, AppInput>
export let componentContainerHeight: number
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
export let horizontal: boolean = false
export let panes: number[]
export const staticOutputs: string[] = []
const { app, focusedGrid, selectedComponent } = getContext<AppEditorContext>('AppEditorContext')
let noPadding: boolean | undefined = undefined
function onFocus() {
$focusedGrid = {
parentComponentId: id,
subGridIndex: 0
}
}
$: $selectedComponent === id && onFocus()
$: css = concatCustomCss($app.css?.containercomponent, customCss)
let sumedup = [50, 50]
$: {
let ns = panes.map((x) => (x / panes.reduce((a, b) => a + b, 0)) * 100)
if (!deepEqual(ns, sumedup)) {
sumedup = ns
}
}
</script>
<InputValue {id} input={configuration.noPadding} bind:value={noPadding} />
<div class="h-full w-full border" on:pointerdown|stopPropagation>
<Splitpanes {horizontal}>
{#each sumedup as paneSize, index (index)}
<Pane size={paneSize} minSize={20}>
{#if $app.subgrids?.[`${id}-${index}`]}
<SubGridEditor
noYPadding
{noPadding}
{id}
shouldHighlight={$focusedGrid?.subGridIndex === index}
class={css?.container.class}
style={css?.container.style}
bind:subGrid={$app.subgrids[`${id}-${index}`]}
containerHeight={horizontal
? (componentContainerHeight * paneSize) / 100 - 1
: componentContainerHeight - 8}
on:focus={() => {
$selectedComponent = id
$focusedGrid = {
parentComponentId: id,
subGridIndex: index
}
}}
/>
{/if}
</Pane>
{/each}
</Splitpanes>
</div>

View File

@@ -4,14 +4,17 @@
import SubGridEditor from '../../editor/SubGridEditor.svelte'
import type { AppInput } from '../../inputType'
import type { Output } from '../../rx'
import type { AppEditorContext, GridItem } from '../../types'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import { concatCustomCss } from '../../utils'
import InputValue from '../helpers/InputValue.svelte'
export let id: string
export let configuration: Record<string, AppInput>
export let subGrids: GridItem[][] | undefined = undefined
export let componentContainerHeight: number
export let tabs: string[]
export let customCss:
| ComponentCustomCSS<'tabRow' | 'tabs' | 'selectedTab' | 'container'>
| undefined = undefined
export const staticOutputs: string[] = ['selectedTabIndex']
const { app, worldStore, focusedGrid, selectedComponent } =
@@ -30,8 +33,19 @@
selectedTabIndex: Output<number | null>
}
$: outputs?.selectedTabIndex && handleOutputs()
function handleTabSelection() {
outputs?.selectedTabIndex.set(selectedIndex)
if ($selectedComponent != id) {
$selectedComponent = id
}
if ($focusedGrid?.parentComponentId != id || $focusedGrid?.subGridIndex != selectedIndex) {
$focusedGrid = {
parentComponentId: id,
subGridIndex: selectedIndex
}
}
}
$: selectedIndex >= 0 && handleTabSelection()
@@ -46,29 +60,46 @@
}
$: $selectedComponent === id && selectedIndex >= 0 && onFocus()
$: css = concatCustomCss($app.css?.tabscomponent, customCss)
function handleOutputs() {
outputs?.selectedTabIndex.set(outputs.selectedTabIndex.peak())
}
</script>
<InputValue {id} input={configuration.tabs} bind:value={tabs} />
<InputValue {id} input={configuration.noPadding} bind:value={noPadding} />
<div>
<div bind:clientHeight={tabHeight}>
<Tabs bind:selected>
<div bind:clientHeight={tabHeight} on:pointerdown|stopPropagation>
<Tabs bind:selected class={css?.tabRow?.class} style={css?.tabRow?.style}>
{#each tabs ?? [] as res}
<Tab value={res}>
<Tab
value={res}
class={css?.tabs?.class}
style={css?.tabs?.style}
selectedClass={css?.selectedTab?.class}
selectedStyle={css?.selectedTab?.style}
>
<span class="font-semibold">{res}</span>
</Tab>
{/each}
</Tabs>
</div>
{#if $app.subgrids?.[`${id}-${selectedIndex}`]}
<SubGridEditor
bind:subGrid={$app.subgrids[`${id}-${selectedIndex}`]}
{noPadding}
containerHeight={componentContainerHeight - tabHeight}
on:focus={() => {
$selectedComponent = id
}}
/>
{#if $app.subgrids}
{#each tabs ?? [] as res, i}
<SubGridEditor
{id}
visible={i === selectedIndex}
bind:subGrid={$app.subgrids[`${id}-${i}`]}
{noPadding}
class={css?.container?.class}
style={css?.container?.style}
containerHeight={componentContainerHeight - tabHeight}
on:focus={() => {
$selectedComponent = id
}}
/>
{/each}
{/if}
</div>

View File

@@ -1,3 +1,5 @@
export { default as AppContainer } from './AppContainer.svelte'
export { default as AppDivider } from './AppDivider.svelte'
export { default as AppTabs } from './AppTabs.svelte'
export { default as AppDrawer } from './AppDrawer.svelte'
export { default as AppSplitpanes } from './AppSplitpanes.svelte'
export { default as AppTabs } from './AppTabs.svelte'

View File

@@ -24,7 +24,7 @@
import Icon from 'svelte-awesome'
import { faCode, faPlus, faSliders } from '@fortawesome/free-solid-svg-icons'
import ContextPanel from './contextPanel/ContextPanel.svelte'
import { encodeState } from '$lib/utils'
import { classNames, encodeState } from '$lib/utils'
import AppPreview from './AppPreview.svelte'
import { userStore, workspaceStore } from '$lib/stores'
@@ -36,7 +36,6 @@
import UnsavedConfirmationModal from '$lib/components/common/confirmationModal/UnsavedConfirmationModal.svelte'
import { page } from '$app/stores'
import CssSettings from './componentsPanel/CssSettings.svelte'
import { findGridItem } from './appUtils'
export let app: App
export let path: string
@@ -60,14 +59,12 @@
const runnableComponents = writable<Record<string, () => Promise<void>>>({})
const errorByComponent = writable<Record<string, { error: string; componentId: string }>>({})
const focusedGrid = writable<FocusedGrid | undefined>(undefined)
setContext<AppEditorContext>('AppEditorContext', {
worldStore,
staticOutputs,
app: appStore,
lazyGrid: writable([]),
summary: summaryStore,
selectedComponent,
mode,
@@ -83,7 +80,9 @@
noBackend: false,
errorByComponent,
openDebugRun: writable(undefined),
focusedGrid
focusedGrid,
stateId: writable(0),
parentWidth: writable(0)
})
let timeout: NodeJS.Timeout | undefined = undefined
@@ -127,7 +126,7 @@
{#if $connectingInput.opened}
<div
class="absolute w-full h-screen bg-black border-2 bg-opacity-25 z-20 flex justify-center items-center"
class="absolute w-full h-screen bg-black border-2 bg-opacity-25 z-20 flex justify-center items-center"
/>
{/if}
{#if !$userStore?.operator}
@@ -139,7 +138,7 @@
{#if previewing}
<SplitPanesWrapper>
<div
class={twMerge('bg-gray-100 h-full w-full', $appStore.css?.['app']?.['viewer']?.class)}
class={twMerge('h-full w-full', $appStore.css?.['app']?.['viewer']?.class)}
style={$appStore.css?.['app']?.['viewer']?.style}
>
<AppPreview
@@ -166,6 +165,10 @@
<Splitpanes horizontal>
<Pane size={$connectingInput?.opened ? 100 : 70}>
<div
on:pointerdown={(e) => {
$selectedComponent = undefined
$focusedGrid = undefined
}}
class={twMerge(
'bg-gray-100 h-full w-full',
$appStore.css?.['app']?.['viewer']?.class
@@ -173,14 +176,17 @@
style={$appStore.css?.['app']?.['viewer']?.style}
>
<div
class="relative mx-auto w-full h-full overflow-auto {app.fullscreen
? ''
: 'max-w-6xl'}"
class={classNames(
'relative mx-auto w-full h-full overflow-auto',
app.fullscreen ? '' : 'max-w-6xl'
)}
>
{#if $appStore.grid}
<div class={width}>
<div on:pointerdown|stopPropagation class={width}>
<GridEditor {policy} />
</div>
<div id="app-editor-top-level-drawer" />
{/if}
</div>
</div>

View File

@@ -51,7 +51,7 @@
UserAppInput
} from '../inputType'
import type { AppEditorContext } from '../types'
import { toStatic } from '../utils'
import { allItems, toStatic } from '../utils'
import AppExportButton from './AppExportButton.svelte'
import AppInputs from './AppInputs.svelte'
import type { AppComponent } from './component/components'
@@ -124,7 +124,7 @@
}
async function computeTriggerables() {
const allTriggers = await Promise.all(
$app.grid
allItems($app.grid, $app.subgrids)
.flatMap((x) => {
let c = x.data as AppComponent
let r: (AppInput | undefined)[] = [c.componentInput]
@@ -293,7 +293,7 @@
</PanelSection>
</Pane>
<Pane size={75}>
<div class="h-full w-full overflow-hidden">
<div class="h-full w-full overflow-auto">
{#if selectedJobId}
{#if !job}
<Skeleton layout={[[40]]} />

View File

@@ -6,7 +6,7 @@
import AppComponentInput from './AppComponentInput.svelte'
import InputsSpecsEditor from './settingsPanel/InputsSpecsEditor.svelte'
const { app, lazyGrid } = getContext<AppEditorContext>('AppEditorContext')
const { app } = getContext<AppEditorContext>('AppEditorContext')
let resourceOnly: boolean = true
</script>
@@ -18,9 +18,9 @@
<Toggle bind:checked={resourceOnly} options={{ right: 'Resource only' }} />
</div>
<div class="gap-4 flex flex-col pt-4">
{#each $lazyGrid as gridItem (gridItem.data.id)}
{#each $app.grid as gridItem (gridItem.data.id)}
<div>
{#if gridItem.data.type === 'tablecomponent'}
{#if gridItem?.data?.type === 'tablecomponent'}
<div>
<AppComponentInput bind:component={gridItem.data} {resourceOnly} />
<div class="ml-4 mt-4">

Some files were not shown because too many files have changed in this diff Show More