Merge branch 'main' into di/assets

This commit is contained in:
Diego Imbert
2025-07-03 00:28:56 +02:00
committed by GitHub
34 changed files with 490 additions and 243 deletions
@@ -1,4 +1,4 @@
name: Update SQLx
name: Git commands
on:
issue_comment:
@@ -103,3 +103,73 @@ jobs:
repo: context.repo.repo,
body: 'Successfully ran sqlx update'
})
update-ee-ref:
if: github.event.issue.pull_request && startsWith(github.event.comment.body, '/eeref')
runs-on: ubicloud-standard-2
permissions:
contents: write
pull-requests: write
issues: write
steps:
- name: Comment on PR - Starting
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: 'Starting ee ref update...'
})
- name: Checkout repository
uses: actions/checkout@v3
with:
ref: ${{ github.event.issue.pull_request.head.ref }}
fetch-depth: 0
- name: Checkout windmill-ee-private
uses: actions/checkout@v3
with:
repository: windmill-labs/windmill-ee-private
path: windmill-ee-private
token: ${{ secrets.WINDMILL_EE_PRIVATE_ACCESS }}
- name: Get last commit hash of private-repo
id: get-commit-hash
run: |
cd windmill-ee-private
COMMIT_HASH=$(git rev-parse HEAD)
echo "commit_hash=$COMMIT_HASH" >> $GITHUB_OUTPUT
echo "Latest commit hash: $COMMIT_HASH"
- name: Update ee-repo-ref.txt
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo "${{ steps.get-commit-hash.outputs.commit_hash }}" > backend/ee-repo-ref.txt
echo "Updated backend/ee-repo-ref.txt with commit hash: ${{ steps.get-commit-hash.outputs.commit_hash }}"
# commit and push the changes
PR_NUMBER=${{ github.event.issue.number }}
BRANCH_NAME=$(gh pr view $PR_NUMBER --json headRefName --jq .headRefName)
echo "Checking out PR branch: $BRANCH_NAME"
git checkout $BRANCH_NAME
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git add backend/ee-repo-ref.txt
git commit -m "Update ee-repo-ref.txt" || echo "No changes to commit"
git push origin $BRANCH_NAME
- name: Comment on PR - Completed
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: 'Successfully updated ee-repo-ref.txt'
})
+9
View File
@@ -1,5 +1,14 @@
# Changelog
## [1.502.2](https://github.com/windmill-labs/windmill/compare/v1.502.1...v1.502.2) (2025-07-01)
### Bug Fixes
* bad spacing ai chat context elements ([#6111](https://github.com/windmill-labs/windmill/issues/6111)) ([2fb912b](https://github.com/windmill-labs/windmill/commit/2fb912b78c90d9e70d3db4b0c3c473831161c8b4))
* **frontend:** improve step job load ([#6109](https://github.com/windmill-labs/windmill/issues/6109)) ([0afe3f9](https://github.com/windmill-labs/windmill/commit/0afe3f9691d93f837b10b29a0cf125eaa175589d))
* **frontend:** only show test button for script modules ([#6107](https://github.com/windmill-labs/windmill/issues/6107)) ([7042a6f](https://github.com/windmill-labs/windmill/commit/7042a6f52db823d6b9b5ad14fa83af36880bd2d5))
## [1.502.1](https://github.com/windmill-labs/windmill/compare/v1.502.0...v1.502.1) (2025-07-01)
+53 -53
View File
@@ -635,7 +635,7 @@ dependencies = [
"bytes",
"http 1.3.1",
"rand 0.8.5",
"reqwest 0.12.21",
"reqwest 0.12.22",
"serde",
"serde-aux",
"serde_json",
@@ -746,9 +746,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "aws-config"
version = "1.8.0"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "455e9fb7743c6f6267eb2830ccc08686fbb3d13c9a689369562fd4d4ef9ea462"
checksum = "c18d005c70d2b9c0c1ea8876c039db0ec7fb71164d25c73ccea21bf41fd02171"
dependencies = [
"aws-credential-types",
"aws-runtime",
@@ -835,9 +835,9 @@ dependencies = [
[[package]]
name = "aws-sdk-sqs"
version = "1.73.0"
version = "1.74.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f3d3cf0d52a50e0ac3fada7e0db27a2172b986f833d061def1df78c7dbe80ae"
checksum = "256b8f7caffe3240a543f60409be9bba23038ceef5933da63e09d89197fc2333"
dependencies = [
"aws-credential-types",
"aws-runtime",
@@ -857,9 +857,9 @@ dependencies = [
[[package]]
name = "aws-sdk-sso"
version = "1.73.0"
version = "1.74.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ac1674cba7872061a29baaf02209fefe499ff034dfd91bd4cc59e4d7741489"
checksum = "e0a69de9c1b9272da2872af60c7402683e7f45c06267735b4332deacb203239b"
dependencies = [
"aws-credential-types",
"aws-runtime",
@@ -879,9 +879,9 @@ dependencies = [
[[package]]
name = "aws-sdk-ssooidc"
version = "1.74.0"
version = "1.75.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a6a22f077f5fd3e3c0270d4e1a110346cddf6769e9433eb9e6daceb4ca3b149"
checksum = "f0b161d836fac72bdd5ac1a4cd1cdc38ab888c7af26cfd95f661be4409505e63"
dependencies = [
"aws-credential-types",
"aws-runtime",
@@ -901,9 +901,9 @@ dependencies = [
[[package]]
name = "aws-sdk-sts"
version = "1.75.0"
version = "1.76.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3258fa707f2f585ee3049d9550954b959002abd59176975150a01d5cf38ae3f"
checksum = "cb1cd79a3412751a341a28e2cd0d6fa4345241976da427b075a0c0cd5409f886"
dependencies = [
"aws-credential-types",
"aws-runtime",
@@ -5855,7 +5855,7 @@ dependencies = [
"google-cloud-token",
"home",
"jsonwebtoken 9.3.1",
"reqwest 0.12.21",
"reqwest 0.12.22",
"serde",
"serde_json",
"thiserror 1.0.69",
@@ -5898,7 +5898,7 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d901aeb453fd80e51d64df4ee005014f6cf39f2d736dd64f7239c132d9d39a6a"
dependencies = [
"reqwest 0.12.21",
"reqwest 0.12.22",
"thiserror 1.0.69",
"tokio",
]
@@ -6182,7 +6182,7 @@ dependencies = [
"native-tls",
"num_cpus",
"rand 0.9.0",
"reqwest 0.12.21",
"reqwest 0.12.22",
"serde",
"serde_json",
"thiserror 2.0.12",
@@ -8516,7 +8516,7 @@ dependencies = [
"getrandom 0.2.16",
"http 1.3.1",
"rand 0.8.5",
"reqwest 0.12.21",
"reqwest 0.12.22",
"serde",
"serde_json",
"serde_path_to_error",
@@ -8565,7 +8565,7 @@ dependencies = [
"percent-encoding",
"quick-xml 0.37.5",
"rand 0.9.0",
"reqwest 0.12.21",
"reqwest 0.12.22",
"ring 0.17.14",
"rustls-pemfile 2.2.0",
"serde",
@@ -10265,9 +10265,9 @@ dependencies = [
[[package]]
name = "reqwest"
version = "0.12.21"
version = "0.12.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c8cea6b35bcceb099f30173754403d2eba0a5dc18cea3630fccd88251909288"
checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531"
dependencies = [
"async-compression",
"base64 0.22.1",
@@ -10321,7 +10321,7 @@ dependencies = [
"anyhow",
"async-trait",
"http 1.3.1",
"reqwest 0.12.21",
"reqwest 0.12.22",
"serde",
"thiserror 1.0.69",
"tower-service",
@@ -10340,7 +10340,7 @@ dependencies = [
"http 1.3.1",
"hyper 1.6.0",
"parking_lot 0.11.2",
"reqwest 0.12.21",
"reqwest 0.12.22",
"reqwest-middleware",
"retry-policies",
"thiserror 1.0.69",
@@ -13627,7 +13627,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba3beec919fbdf99d719de8eda6adae3281f8a5b71ae40431f44dc7423053d34"
dependencies = [
"loki-api",
"reqwest 0.12.21",
"reqwest 0.12.22",
"serde",
"serde_json",
"snap",
@@ -14629,7 +14629,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windmill"
version = "1.502.1"
version = "1.502.2"
dependencies = [
"anyhow",
"axum",
@@ -14649,7 +14649,7 @@ dependencies = [
"prometheus",
"quote",
"rand 0.9.0",
"reqwest 0.12.21",
"reqwest 0.12.22",
"rustls 0.23.28",
"serde",
"serde_json",
@@ -14681,7 +14681,7 @@ dependencies = [
[[package]]
name = "windmill-api"
version = "1.502.1"
version = "1.502.2"
dependencies = [
"anyhow",
"argon2",
@@ -14741,7 +14741,7 @@ dependencies = [
"rand 0.9.0",
"rdkafka",
"regex",
"reqwest 0.12.21",
"reqwest 0.12.22",
"rmcp",
"rsa",
"rumqttc",
@@ -14792,7 +14792,7 @@ dependencies = [
[[package]]
name = "windmill-api-client"
version = "1.502.1"
version = "1.502.2"
dependencies = [
"base64 0.22.1",
"chrono",
@@ -14807,7 +14807,7 @@ dependencies = [
[[package]]
name = "windmill-audit"
version = "1.502.1"
version = "1.502.2"
dependencies = [
"chrono",
"serde",
@@ -14820,7 +14820,7 @@ dependencies = [
[[package]]
name = "windmill-autoscaling"
version = "1.502.1"
version = "1.502.2"
dependencies = [
"anyhow",
"serde",
@@ -14834,7 +14834,7 @@ dependencies = [
[[package]]
name = "windmill-common"
version = "1.502.1"
version = "1.502.2"
dependencies = [
"anyhow",
"async-stream",
@@ -14876,7 +14876,7 @@ dependencies = [
"quick_cache",
"rand 0.9.0",
"regex",
"reqwest 0.12.21",
"reqwest 0.12.22",
"reqwest-middleware",
"reqwest-retry",
"semver 1.0.26",
@@ -14911,7 +14911,7 @@ dependencies = [
[[package]]
name = "windmill-git-sync"
version = "1.502.1"
version = "1.502.2"
dependencies = [
"regex",
"serde",
@@ -14925,7 +14925,7 @@ dependencies = [
[[package]]
name = "windmill-indexer"
version = "1.502.1"
version = "1.502.2"
dependencies = [
"anyhow",
"bytes",
@@ -14948,7 +14948,7 @@ dependencies = [
[[package]]
name = "windmill-macros"
version = "1.502.1"
version = "1.502.2"
dependencies = [
"itertools 0.14.0",
"lazy_static",
@@ -14960,7 +14960,7 @@ dependencies = [
[[package]]
name = "windmill-parser"
version = "1.502.1"
version = "1.502.2"
dependencies = [
"convert_case 0.6.0",
"serde",
@@ -14969,7 +14969,7 @@ dependencies = [
[[package]]
name = "windmill-parser-bash"
version = "1.502.1"
version = "1.502.2"
dependencies = [
"anyhow",
"lazy_static",
@@ -14981,7 +14981,7 @@ dependencies = [
[[package]]
name = "windmill-parser-csharp"
version = "1.502.1"
version = "1.502.2"
dependencies = [
"anyhow",
"serde_json",
@@ -14993,7 +14993,7 @@ dependencies = [
[[package]]
name = "windmill-parser-go"
version = "1.502.1"
version = "1.502.2"
dependencies = [
"anyhow",
"gosyn",
@@ -15005,7 +15005,7 @@ dependencies = [
[[package]]
name = "windmill-parser-graphql"
version = "1.502.1"
version = "1.502.2"
dependencies = [
"anyhow",
"lazy_static",
@@ -15017,7 +15017,7 @@ dependencies = [
[[package]]
name = "windmill-parser-java"
version = "1.502.1"
version = "1.502.2"
dependencies = [
"anyhow",
"serde_json",
@@ -15029,7 +15029,7 @@ dependencies = [
[[package]]
name = "windmill-parser-nu"
version = "1.502.1"
version = "1.502.2"
dependencies = [
"anyhow",
"nu-parser",
@@ -15040,7 +15040,7 @@ dependencies = [
[[package]]
name = "windmill-parser-php"
version = "1.502.1"
version = "1.502.2"
dependencies = [
"anyhow",
"itertools 0.14.0",
@@ -15051,7 +15051,7 @@ dependencies = [
[[package]]
name = "windmill-parser-py"
version = "1.502.1"
version = "1.502.2"
dependencies = [
"anyhow",
"itertools 0.14.0",
@@ -15062,7 +15062,7 @@ dependencies = [
[[package]]
name = "windmill-parser-py-imports"
version = "1.502.1"
version = "1.502.2"
dependencies = [
"anyhow",
"async-recursion",
@@ -15085,7 +15085,7 @@ dependencies = [
[[package]]
name = "windmill-parser-rust"
version = "1.502.1"
version = "1.502.2"
dependencies = [
"anyhow",
"convert_case 0.6.0",
@@ -15102,7 +15102,7 @@ dependencies = [
[[package]]
name = "windmill-parser-sql"
version = "1.502.1"
version = "1.502.2"
dependencies = [
"anyhow",
"lazy_static",
@@ -15115,7 +15115,7 @@ dependencies = [
[[package]]
name = "windmill-parser-ts"
version = "1.502.1"
version = "1.502.2"
dependencies = [
"anyhow",
"lazy_static",
@@ -15133,7 +15133,7 @@ dependencies = [
[[package]]
name = "windmill-parser-wasm"
version = "1.502.1"
version = "1.502.2"
dependencies = [
"anyhow",
"getrandom 0.2.16",
@@ -15157,7 +15157,7 @@ dependencies = [
[[package]]
name = "windmill-parser-yaml"
version = "1.502.1"
version = "1.502.2"
dependencies = [
"anyhow",
"serde_json",
@@ -15167,7 +15167,7 @@ dependencies = [
[[package]]
name = "windmill-queue"
version = "1.502.1"
version = "1.502.2"
dependencies = [
"anyhow",
"async-recursion",
@@ -15184,7 +15184,7 @@ dependencies = [
"lazy_static",
"prometheus",
"regex",
"reqwest 0.12.21",
"reqwest 0.12.22",
"serde",
"serde_json",
"serde_urlencoded",
@@ -15200,7 +15200,7 @@ dependencies = [
[[package]]
name = "windmill-sql-datatype-parser-wasm"
version = "1.502.1"
version = "1.502.2"
dependencies = [
"wasm-bindgen",
"wasm-bindgen-test",
@@ -15210,7 +15210,7 @@ dependencies = [
[[package]]
name = "windmill-worker"
version = "1.502.1"
version = "1.502.2"
dependencies = [
"anyhow",
"async-recursion",
@@ -15262,7 +15262,7 @@ dependencies = [
"prometheus",
"rand 0.9.0",
"regex",
"reqwest 0.12.21",
"reqwest 0.12.22",
"reqwest-middleware",
"rust_decimal",
"serde",
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "windmill"
version = "1.502.1"
version = "1.502.2"
authors.workspace = true
edition.workspace = true
@@ -32,7 +32,7 @@ members = [
]
[workspace.package]
version = "1.502.1"
version = "1.502.2"
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
edition = "2021"
+83
View File
@@ -2089,6 +2089,89 @@ def main():
assert_eq!(result, serde_json::json!("hello world"));
}
#[cfg(feature = "python")]
#[sqlx::test(fixtures("base"))]
async fn test_python_global_site_packages(db: Pool<Postgres>) {
use windmill_common::{cache::concatcp, worker::ROOT_CACHE_DIR};
initialize_tracing().await;
let server = ApiServer::start(db.clone()).await;
let port = server.addr.port();
// Shared for all 3.12.*
let path = concatcp!(ROOT_CACHE_DIR, "python_3_12/global-site-packages").to_owned();
std::fs::create_dir_all(&path).unwrap();
std::fs::write(path + "/my_global_site_package_3_12_any.py", "").unwrap();
// 3.12
{
let content = r#"# py: ==3.12
#requirements:
#
import my_global_site_package_3_12_any
def main():
return "hello world"
"#
.to_owned();
let job = JobPayload::Code(RawCode {
hash: None,
content,
path: None,
language: ScriptLang::Python3,
lock: None,
custom_concurrency_key: None,
concurrent_limit: None,
concurrency_time_window_s: None,
cache_ttl: None,
dedicated_worker: None,
});
let result = run_job_in_new_worker_until_complete(&db, job, port)
.await
.json_result()
.unwrap();
assert_eq!(result, serde_json::json!("hello world"));
}
// 3.12.1
{
let content = r#"# py: ==3.12.1
#requirements:
#
import my_global_site_package_3_12_any
def main():
return "hello world"
"#
.to_owned();
let job = JobPayload::Code(RawCode {
hash: None,
content,
path: None,
language: ScriptLang::Python3,
lock: None,
custom_concurrency_key: None,
concurrent_limit: None,
concurrency_time_window_s: None,
cache_ttl: None,
dedicated_worker: None,
});
let result = run_job_in_new_worker_until_complete(&db, job, port)
.await
.json_result()
.unwrap();
assert_eq!(result, serde_json::json!("hello world"));
}
}
#[cfg(feature = "python")]
#[sqlx::test(fixtures("base"))]
async fn test_python_job_heavy_dep(db: Pool<Postgres>) {
+1 -1
View File
@@ -1,7 +1,7 @@
openapi: "3.0.3"
info:
version: 1.502.1
version: 1.502.2
title: Windmill API
contact:
@@ -10,9 +10,9 @@ use windmill_queue::{CanceledBy, MiniPulledJob};
use serde::Deserialize;
use crate::common::build_args_map;
use crate::common::{build_http_client, resolve_job_timeout, OccupancyMetrics};
use crate::handle_child::run_future_with_polling_update_job_poller;
use crate::common::build_args_map;
use windmill_common::client::AuthedClient;
#[derive(Deserialize)]
@@ -107,6 +107,20 @@ pub async fn do_graphql(
.await
.map_err(|e| Error::ExecutionErr(e.to_string()))?;
// Check HTTP status before processing response
if !response.status().is_success() {
let status = response.status();
let error_body = response
.text()
.await
.unwrap_or_else(|_| "Failed to read error response".to_string());
return Err(Error::ExecutionErr(format!(
"GraphQL request failed with HTTP {}: {}",
status.as_u16(),
error_body
)));
}
let result_stream = response.bytes_stream();
let mut i = 0;
+1 -1
View File
@@ -2,7 +2,7 @@ import { sleep } from "https://deno.land/x/sleep@v1.2.1/mod.ts";
import * as windmill from "https://deno.land/x/windmill@v1.174.0/mod.ts";
import * as api from "https://deno.land/x/windmill@v1.174.0/windmill-api/index.ts";
export const VERSION = "v1.502.1";
export const VERSION = "v1.502.2";
export async function login(email: string, password: string): Promise<string> {
return await windmill.UserService.login({
+1 -1
View File
@@ -63,7 +63,7 @@ export {
// }
// });
export const VERSION = "1.502.1";
export const VERSION = "1.502.2";
const command = new Command()
.name("wmill")
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "windmill-components",
"version": "1.502.1",
"version": "1.502.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "windmill-components",
"version": "1.502.1",
"version": "1.502.2",
"hasInstallScript": true,
"license": "AGPL-3.0",
"dependencies": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "windmill-components",
"version": "1.502.1",
"version": "1.502.2",
"scripts": {
"dev": "vite dev",
"build": "vite build",
+1 -1
View File
@@ -762,7 +762,7 @@ JsonNode ${windmillPathToCamelCaseName(path)} = JsonNode.Parse(await client.GetS
{/if}
{#if customUi?.assistants != false}
{#if lang == 'deno' || lang == 'python3' || lang == 'go' || lang == 'bash' || lang == 'nu'}
{#if lang == 'deno' || lang == 'python3' || lang == 'go' || lang == 'bash'}
<Button
aiId="editor-bar-reload-assistants"
aiDescription="Reload assistants"
@@ -24,7 +24,7 @@
disableMock?: boolean
disableHistory?: boolean
onUpdateMock?: (mock: { enabled: boolean; return_value?: unknown }) => void
loadingHistory?: boolean
loadingJob?: boolean
}
let {
@@ -40,26 +40,15 @@
disableMock = false,
disableHistory = false,
onUpdateMock,
loadingHistory = false
loadingJob = false
}: Props = $props()
const { testSteps } = getContext<FlowEditorContext>('FlowEditorContext')
let selectedJob: Job | undefined = $state(undefined)
let fetchingLastJob = false
let preview: 'mock' | 'job' | undefined = $state(undefined)
let jobProgressReset: () => void = $state(() => {})
let nlastJob = $derived.by(() => {
if (testJob && testJob.type === 'CompletedJob') {
return { ...testJob, preview: true }
}
if (lastJob) {
return { ...lastJob, preview: false }
}
return undefined
})
let forceJson = $state(false)
const logJob = $derived(testJob ?? selectedJob)
@@ -77,7 +66,8 @@
{/if}
<OutputPickerInner
lastJob={nlastJob}
{lastJob}
{testJob}
fullResult
moduleId={mod.id}
closeOnOutsideClick={true}
@@ -86,7 +76,7 @@
mock={mod.mock}
bind:forceJson
bind:selectedJob
isLoading={(testIsLoading && !scriptProgress) || fetchingLastJob || loadingHistory}
isLoading={testIsLoading || loadingJob}
bind:preview
path={`path` in mod.value ? mod.value.path : ''}
{loopStatus}
@@ -116,9 +106,7 @@
duration={logJob?.['duration_ms']}
mem={logJob?.['mem_peak']}
content={logJob?.logs}
isLoading={(testIsLoading && logJob?.['running'] == false) ||
fetchingLastJob ||
loadingHistory}
isLoading={(testIsLoading && logJob?.['running'] == false) || loadingJob}
tag={logJob?.tag}
/>
{/if}
@@ -1,7 +1,7 @@
<script lang="ts" module>
type testModuleState = {
loading: boolean
cancel?: () => void
cancel?: () => Promise<void>
}
let testModulesState = $state<Record<string, testModuleState>>({})
@@ -52,7 +52,10 @@
// Not defined if JobProgressBar not loaded
if (jobProgressReset) jobProgressReset()
testModulesState[mod.id].cancel = testJobLoader?.cancelJob
testModulesState[mod.id].cancel = async () => {
await testJobLoader?.cancelJob()
testJob = undefined
}
const val = mod.value
// let jobId: string | undefined = undefined
@@ -1,5 +1,5 @@
<script lang="ts">
import { getContext } from 'svelte'
import { getContext, untrack } from 'svelte'
import { initConfig, initOutput } from '../../editor/appUtils'
import SubGridEditor from '../../editor/SubGridEditor.svelte'
import type { AppViewerContext, ComponentCustomCSS, RichConfigurations } from '../../types'
@@ -36,7 +36,7 @@
componentContainerHeight
}: Props = $props()
const { app, focusedGrid, selectedComponent, worldStore, connectingInput } =
const { app, focusedGrid, selectedComponent, worldStore, connectingInput, componentControl } =
getContext<AppViewerContext>('AppViewerContext')
let everRender = $state(render)
@@ -44,10 +44,11 @@
render && !everRender && (everRender = true)
})
const outputs = initOutput($worldStore, id, {
let outputs = initOutput($worldStore, id, {
result: undefined,
loading: false,
inputs: {}
inputs: {},
currentIndex: 0
})
const resolvedConfig = $state(
@@ -76,6 +77,41 @@
})
})
let currentPageIndex = $state(0)
// Single update function - ONLY place that calls .set()
function handleIndexChange() {
if (outputs?.currentIndex) {
if (!Array.isArray(result) || result.length === 0) {
// No data or empty data - reset to 0
currentPageIndex = 0
outputs.currentIndex.set(0)
} else if (currentPageIndex >= result.length) {
// Current index is out of bounds - reset to 0
currentPageIndex = 0
outputs.currentIndex.set(0)
} else {
// Valid data and valid current index
outputs.currentIndex.set(currentPageIndex)
// Navigate carousel to match the current index
if (carousel) {
carousel.goTo(currentPageIndex)
}
}
}
}
// Watch for changes and call update function (like working components)
$effect.pre(() => {
currentPageIndex != undefined && untrack(() => handleIndexChange())
})
$componentControl[id] = {
setSelectedIndex: (index: number) => {
if (Array.isArray(result) && index >= 0 && index < result.length) {
currentPageIndex = index
}
}
}
</script>
{#each Object.keys(components['carousellistcomponent'].initialData.configuration) as key (key)}
@@ -1319,6 +1319,7 @@ export function isContainer(type: string): boolean {
type === 'horizontalsplitpanescomponent' ||
type === 'steppercomponent' ||
type === 'listcomponent' ||
type === 'carousellistcomponent' ||
type === 'decisiontreecomponent'
)
}
@@ -1336,6 +1337,9 @@ export function subGridIndexKey(type: string | undefined, id: string, world: Wor
case 'steppercomponent': {
return (world?.outputsById?.[id]?.currentStepIndex?.peak() as number) ?? 0
}
case 'carousellistcomponent': {
return (world?.outputsById?.[id]?.currentIndex?.peak() as number) ?? 0
}
case 'decisiontreecomponent': {
return (world?.outputsById?.[id]?.currentNodeIndex?.peak() as number) ?? 0
}
@@ -38,7 +38,8 @@ const setValue = {
const setSelectedIndex = {
title: 'setSelectedIndex',
description: 'Use the setSelectedIndex function to select a row in a table or an AG Grid table.',
description:
'Use the setSelectedIndex function to select a row in a table, an AG Grid table, or navigate to a slide in a Carousel component.',
example: 'setSelectedIndex(id: string, index: number)',
documentation: 'https://www.windmill.dev/docs/apps/app-runnable-panel#setselectedindex'
}
@@ -90,6 +91,8 @@ export function getComponentControl(type: keyof typeof components): Array<Compon
return [setTab, setValue, setSelectedIndex]
case 'selecttabcomponent':
return [setTab, setValue, setSelectedIndex]
case 'carousellistcomponent':
return [setSelectedIndex]
case 'conditionalwrapper':
case 'steppercomponent':
return [setTab]
@@ -20,8 +20,6 @@
let { component = $bindable(), children }: Props = $props()
let convertToUIEditorCallback: (() => void) | undefined = $state(undefined)
console.log('FOOBAR', component.type)
let selected = $state(
(component.type === 'plotlycomponentv2' || component.type === 'chartjscomponentv2') &&
component.datasets !== undefined
@@ -208,67 +208,68 @@
>
<div class="flex flex-col" bind:clientHeight={height}>
{#each messages as message, messageIndex}
{#if message.role === 'user' && message.contextElements}
<div class="flex flex-row gap-1 mb-1 overflow-scroll no-scrollbar px-2">
{#each message.contextElements as element}
<ContextElementBadge contextElement={element} />
{/each}
</div>
{/if}
<div
class={twMerge(
'text-sm py-1 mx-2',
message.role === 'user' &&
'px-2 border border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-900 rounded-lg mb-2 relative group',
message.role === 'user' && messageIndex > 0 && 'mt-6',
(message.role === 'assistant' || message.role === 'tool') && 'px-[1px]',
message.role === 'tool' && 'text-tertiary'
)}
>
{#if message.role === 'assistant'}
<AssistantMessage {message} />
{:else}
{message.content}
<div class={twMerge(message.role === 'user' && messageIndex > 0 && 'mt-6', 'mb-2')}>
{#if message.role === 'user' && message.contextElements}
<div class="flex flex-row gap-1 mb-1 overflow-scroll no-scrollbar px-2">
{#each message.contextElements as element}
<ContextElementBadge contextElement={element} />
{/each}
</div>
{/if}
<div
class={twMerge(
'text-sm py-1 mx-2',
message.role === 'user' &&
'px-2 border border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-900 rounded-lg relative group',
(message.role === 'assistant' || message.role === 'tool') && 'px-[1px]',
message.role === 'tool' && 'text-tertiary'
)}
>
{#if message.role === 'assistant'}
<AssistantMessage {message} />
{:else}
{message.content}
{/if}
{#if message.role === 'user' && isLastUserMessage(messageIndex) && !aiChatManager.loading}
{#if message.role === 'user' && isLastUserMessage(messageIndex) && !aiChatManager.loading}
<div
class="absolute top-1 right-1 opacity-0 group-hover:opacity-100 transition-opacity"
>
<Button
size="xs2"
variant="border"
color="light"
iconOnly
title="Restart generation"
startIcon={{ icon: RefreshCwIcon }}
btnClasses="!p-1 !h-6 !w-6"
on:click={() => restartGeneration(messageIndex)}
/>
</div>
{/if}
</div>
{#if message.role === 'user' && message.snapshot}
<div
class="absolute top-1 right-1 opacity-0 group-hover:opacity-100 transition-opacity"
class="mx-2 text-sm text-tertiary flex flex-row items-center justify-between gap-2 mt-2"
>
Saved a flow snapshot
<Button
size="xs2"
variant="border"
color="light"
iconOnly
title="Restart generation"
startIcon={{ icon: RefreshCwIcon }}
btnClasses="!p-1 !h-6 !w-6"
on:click={() => restartGeneration(messageIndex)}
/>
on:click={() => {
if (message.snapshot) {
aiChatManager.flowAiChatHelpers?.revertToSnapshot(message.snapshot)
}
}}
title="Revert to snapshot"
startIcon={{ icon: Undo2Icon }}
>
Revert
</Button>
</div>
{/if}
</div>
{#if message.role === 'user' && message.snapshot}
<div
class="mx-2 text-sm text-tertiary flex flex-row items-center justify-between gap-2 mb-2"
>
Saved a flow snapshot
<Button
size="xs2"
variant="border"
color="light"
on:click={() => {
if (message.snapshot) {
aiChatManager.flowAiChatHelpers?.revertToSnapshot(message.snapshot)
}
}}
title="Revert to snapshot"
startIcon={{ icon: Undo2Icon }}
>
Revert
</Button>
</div>
{/if}
{/each}
{#if aiChatManager.loading && !aiChatManager.currentReply}
<div class="mb-6 py-1 px-2">
@@ -4,6 +4,8 @@
import type { ContextElement } from './context'
import AvailableContextList from './AvailableContextList.svelte'
import { aiChatManager } from './AIChatManager.svelte'
import Portal from '$lib/components/Portal.svelte'
import { zIndexes } from '$lib/zIndexes'
interface Props {
availableContext: ContextElement[]
@@ -361,22 +363,24 @@
</div>
{#if showContextTooltip}
<div
bind:this={tooltipElement}
class="absolute bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-md shadow-lg z-50"
style="left: {tooltipPosition.x}px; top: {tooltipPosition.y}px;"
>
<AvailableContextList
{availableContext}
{selectedContext}
onSelect={(element) => {
handleContextSelection(element)
}}
showAllAvailable={true}
stringSearch={contextTooltipWord.slice(1)}
selectedIndex={selectedSuggestionIndex}
/>
</div>
<Portal target="body">
<div
bind:this={tooltipElement}
class="absolute bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-md shadow-lg"
style="left: {tooltipPosition.x}px; top: {tooltipPosition.y}px; z-index: {zIndexes.tooltip};"
>
<AvailableContextList
{availableContext}
{selectedContext}
onSelect={(element) => {
handleContextSelection(element)
}}
showAllAvailable={true}
stringSearch={contextTooltipWord.slice(1)}
selectedIndex={selectedSuggestionIndex}
/>
</div>
</Portal>
{/if}
<style>
@@ -252,7 +252,9 @@
$selectedId && untrack(() => onSelectedIdChange())
})
$effect(() => {
if ($workspaceStore && $pathStore && flowModule?.id && $flowStateStore) {
if (testJob && testJob.type === 'CompletedJob') {
lastJob = $state.snapshot(testJob)
} else if ($workspaceStore && $pathStore && flowModule?.id && $flowStateStore) {
untrack(() => getLastJob())
}
})
@@ -893,7 +895,7 @@
{testIsLoading}
disableMock={preprocessorModule || failureModule}
disableHistory={failureModule}
loadingHistory={stepHistoryLoader?.stepStates[flowModule.id]?.loadingJobs}
loadingJob={stepHistoryLoader?.stepStates[flowModule.id]?.loadingJobs}
/>
</Pane>
{/if}
@@ -76,6 +76,7 @@
inputTransform?: Record<string, any> | undefined
onUpdateMock?: (mock: { enabled: boolean; return_value?: unknown }) => void
onEditInput?: (moduleId: string, key: string) => void
enableTestRun?: boolean
}
let {
@@ -106,7 +107,8 @@
onTestUpTo,
inputTransform,
onUpdateMock,
onEditInput
onEditInput,
enableTestRun = false
}: Props = $props()
let pickableIds: Record<string, any> | undefined = $state(undefined)
@@ -173,17 +175,11 @@
}
$effect(() => {
flowStateStore && $flowStateStore && untrack(() => updateLastJob($flowStateStore))
})
let nlastJob = $derived.by(() => {
if (testJob) {
return { ...testJob, preview: true }
if (testJob && testJob.type === 'CompletedJob') {
lastJob = $state.snapshot(testJob)
} else if (flowStateStore && $flowStateStore) {
untrack(() => updateLastJob($flowStateStore))
}
if (lastJob) {
return { ...lastJob, preview: false }
}
return undefined
})
let isConnectingCandidate = $derived(
@@ -442,7 +438,8 @@
prefix={'results'}
connectingData={isConnecting ? connectingData : undefined}
{mock}
lastJob={nlastJob}
{lastJob}
{testJob}
moduleId={id}
onSelect={selectConnection}
{onUpdateMock}
@@ -462,56 +459,58 @@
</div>
{#if deletable && !action}
<div
class="absolute top-1/2 -translate-y-1/2 -translate-x-[100%] -left-[0] flex items-center w-fit px-2 h-9 min-w-14"
>
{#if (hover || selected) && outputPickerVisible}
<div transition:fade={{ duration: 100 }}>
{#if !testIsLoading}
<Button
size="sm"
color="dark"
title="Run"
btnClasses="p-1.5"
on:click={() => {
outputPicker?.toggleOpen(true)
moduleTest?.loadArgsAndRunTest()
}}
dropdownItems={[
{
label: 'Test up to here',
onClick: () => {
if (id) {
onTestUpTo?.(id)
{#if enableTestRun}
<div
class="absolute top-1/2 -translate-y-1/2 -translate-x-[100%] -left-[0] flex items-center w-fit px-2 h-9 min-w-14"
>
{#if (hover || selected) && outputPickerVisible}
<div transition:fade={{ duration: 100 }}>
{#if !testIsLoading}
<Button
size="sm"
color="dark"
title="Run"
btnClasses="p-1.5"
on:click={() => {
outputPicker?.toggleOpen(true)
moduleTest?.loadArgsAndRunTest()
}}
dropdownItems={[
{
label: 'Test up to here',
onClick: () => {
if (id) {
onTestUpTo?.(id)
}
}
}
}
]}
dropdownBtnClasses="!w-4 px-1"
>
{#if testIsLoading}
<Loader2 size={12} class="animate-spin" />
{:else}
<Play size={12} />
{/if}
</Button>
{:else}
<Button
size="xs"
color="red"
variant="contained"
btnClasses="!h-[25.5px] !w-[44.5px] !p-1.5 gap-0.5"
on:click={async () => {
moduleTest?.cancelJob()
}}
>
<Loader2 size={10} class="animate-spin mr-0.5" />
<X size={14} />
</Button>
{/if}
</div>
{/if}
</div>
]}
dropdownBtnClasses="!w-4 px-1"
>
{#if testIsLoading}
<Loader2 size={12} class="animate-spin" />
{:else}
<Play size={12} />
{/if}
</Button>
{:else}
<Button
size="xs"
color="red"
variant="contained"
btnClasses="!h-[25.5px] !w-[44.5px] !p-1.5 gap-0.5"
on:click={async () => {
moduleTest?.cancelJob()
}}
>
<Loader2 size={10} class="animate-spin mr-0.5" />
<X size={14} />
</Button>
{/if}
</div>
{/if}
</div>
{/if}
<button
class="absolute -top-[10px] -right-[10px] rounded-full h-[20px] w-[20px] trash center-center text-secondary
outline-[1px] outline dark:outline-gray-500 outline-gray-300 bg-surface duration-0 hover:bg-red-400 hover:text-white
@@ -246,6 +246,7 @@
inputTransform={mod.value.type !== 'identity' ? mod.value.input_transforms : undefined}
{onTestUpTo}
{onEditInput}
enableTestRun
>
{#snippet icon()}
<div>
@@ -56,7 +56,8 @@
rightMargin?: boolean
disableMock?: boolean
disableHistory?: boolean
lastJob?: SelectedJob
lastJob?: Job
testJob?: Job
derivedHistoryOpen?: boolean // derived from historyOpen
historyOffset?: any
clazz?: string
@@ -71,6 +72,7 @@
let {
lastJob = undefined,
testJob = undefined,
prefix = '',
allowCopy = false,
connectingData = undefined,
@@ -130,25 +132,25 @@
}
}
function selectJob(job: SelectedJob | undefined) {
if (job && 'result' in job) {
function selectJob(nJob: SelectedJob | undefined) {
if (nJob && 'result' in nJob) {
selectedJob = nJob
} else if (job && 'result' in job) {
selectedJob = job
} else if (lastJob && 'result' in lastJob) {
selectedJob = lastJob
} else {
selectedJob = undefined
}
}
$effect(() => {
if (!lastJob || !('result' in lastJob)) {
if (!job || !('result' in job)) {
return
}
selectJob(lastJob)
selectJob(job)
if (lastJob.preview && mock?.enabled) {
if (job.preview && mock?.enabled) {
preview = 'job'
lastJob.preview = false
job.preview = false
}
})
@@ -254,6 +256,16 @@
}
})
let job = $derived.by(() => {
if (testJob) {
return { ...testJob, preview: testJob.type === 'CompletedJob' }
}
if (lastJob) {
return { ...lastJob, preview: false }
}
return undefined
})
let popoverHeight = $derived(customHeight ?? (clientHeight > 0 ? clientHeight : 0))
const isLoadingAndNotMock = $derived(isLoading && !mock?.enabled)
@@ -324,7 +336,7 @@
togglePreview('mock')
return
}
if (detail.id === lastJob?.id && !mock?.enabled) {
if (detail.id === job?.id && !mock?.enabled) {
togglePreview(undefined)
return
}
@@ -363,7 +375,7 @@
<div
class={twMerge(
'w-grow min-w-0 flex gap-1 items-center h-[27px] rounded-md group',
preview || selectedJob?.id !== lastJob?.id ? 'p-[2px] bg-surface-secondary' : ''
preview || selectedJob?.id !== job?.id ? 'p-[2px] bg-surface-secondary' : ''
)}
>
{#if loopStatus?.type === 'self'}
@@ -399,7 +411,7 @@
job={selectedJob}
class={twMerge(
'min-w-16 text-secondary',
preview || selectedJob?.id !== lastJob?.id ? 'bg-surface shadow-sm h-[23px]' : ''
preview || selectedJob?.id !== job?.id ? 'bg-surface shadow-sm h-[23px]' : ''
)}
/>
{/if}
@@ -686,7 +698,7 @@
pureViewer={false}
/>
{/if}
{:else if !lastJob}
{:else if !job}
<div class="flex flex-col items-center justify-center h-full">
<p class="text-xs text-secondary">
Test this step to see results{#if !disableMock}
+2 -2
View File
@@ -4,8 +4,8 @@ verify_ssl = true
name = "pypi"
[packages]
wmill = ">=1.502.1"
wmill_pg = ">=1.502.1"
wmill = ">=1.502.2"
wmill_pg = ">=1.502.2"
sendgrid = "*"
mysql-connector-python = "*"
pymongo = "*"
+1 -1
View File
@@ -1,7 +1,7 @@
openapi: "3.0.3"
info:
version: 1.502.1
version: 1.502.2
title: OpenFlow Spec
contact:
name: Ruben Fiszel
@@ -12,7 +12,7 @@
RootModule = 'WindmillClient.psm1'
# Version number of this module.
ModuleVersion = '1.502.1'
ModuleVersion = '1.502.2'
# Supported PSEditions
# CompatiblePSEditions = @()
+1 -1
View File
@@ -1,6 +1,6 @@
[tool.poetry]
name = "wmill"
version = "1.502.1"
version = "1.502.2"
description = "A client library for accessing Windmill server wrapping the Windmill client API"
license = "Apache-2.0"
homepage = "https://windmill.dev"
+11
View File
@@ -22,6 +22,17 @@ class S3BufferedReader(BufferedReader):
def __enter__(self):
reader = self._context_manager.__enter__()
if reader.status_code >= 400:
error_bytes = reader.read()
try:
error_text = error_bytes.decode('utf-8')
except UnicodeDecodeError:
error_text = str(error_bytes)
raise httpx.HTTPStatusError(
f"Failed to load S3 file: {reader.status_code} {reader.reason_phrase} - {error_text}",
request=reader.request,
response=reader
)
self._iterator = reader.iter_bytes()
return self
+1 -1
View File
@@ -1,6 +1,6 @@
[tool.poetry]
name = "wmill-pg"
version = "1.502.1"
version = "1.502.2"
description = "An extension client for the wmill client library focused on pg"
license = "Apache-2.0"
homepage = "https://windmill.dev"
+11 -2
View File
@@ -717,7 +717,7 @@ export async function loadS3FileStream(
const queryParams = new URLSearchParams(params);
// We use raw fetch here b/c OpenAPI generated client doesn't handle Blobs nicely
const fileContentBlob = await fetch(
const response = await fetch(
`${
OpenAPI.BASE
}/w/${getWorkspace()}/job_helpers/download_s3_file?${queryParams}`,
@@ -728,7 +728,16 @@ export async function loadS3FileStream(
},
}
);
return fileContentBlob.blob();
// Check if the response was successful
if (!response.ok) {
const errorText = await response.text();
throw new Error(
`Failed to load S3 file: ${response.status} ${response.statusText} - ${errorText}`
);
}
return response.blob();
}
/**
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@windmill/windmill",
"version": "1.502.1",
"version": "1.502.2",
"exports": "./src/index.ts",
"publish": {
"exclude": ["!src", "./s3Types.ts", "./client.ts"]
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "windmill-client",
"description": "Windmill SDK client for browsers and Node.js",
"version": "1.502.1",
"version": "1.502.2",
"author": "Ruben Fiszel",
"license": "Apache 2.0",
"devDependencies": {
+1 -1
View File
@@ -1 +1 @@
1.502.1
1.502.2