Skip to content
Draft
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
4475480
Fix broken batch reruns InputTransformForm
diegoimbert Oct 23, 2025
3aded0e
send alternatives on timeout toast
diegoimbert Oct 23, 2025
7ba995b
Toast when runs page query is slow + throttle toasts spamming
diegoimbert Oct 23, 2025
05806ee
configurable perPage in runs page
diegoimbert Oct 23, 2025
b4130de
store perPage in query params
diegoimbert Oct 23, 2025
86810e7
Merge remote-tracking branch 'origin/main' into di/toast-alternatives…
diegoimbert Oct 24, 2025
6101b45
subtle border
diegoimbert Oct 27, 2025
86d7f04
nit fix
diegoimbert Oct 27, 2025
94d1e60
reduce to 25 actions on runs page
diegoimbert Oct 27, 2025
f2110a8
Merge remote-tracking branch 'origin/main' into di/toast-alternatives…
diegoimbert Oct 27, 2025
79ba675
Merge remote-tracking branch 'origin/main' into di/toast-alternatives…
diegoimbert Oct 29, 2025
7f6560c
Fix annoying scrollbar due to AIChatLayout
diegoimbert Oct 29, 2025
8535c12
nit animated pane
diegoimbert Oct 29, 2025
97f6186
don't kill AI Chat Manager on pane close
diegoimbert Oct 29, 2025
aa436d3
button shrink 0
diegoimbert Oct 29, 2025
961f4c1
Merge branch 'di/aichatlayout-improve' into di/toast-alternatives-on-…
diegoimbert Oct 29, 2025
ac10a50
nist
diegoimbert Oct 29, 2025
366129c
Cancelable Promise Utils
diegoimbert Oct 29, 2025
bd140a1
migrate to CancelablePromiseUtils
diegoimbert Oct 29, 2025
d607f60
CancelablePromise onTimeout + update usage
diegoimbert Oct 30, 2025
afa4381
CancelablePromise onTimeout + update usage
diegoimbert Oct 30, 2025
a7588f6
Loading spinner + fix per page bug in audit logs
diegoimbert Oct 30, 2025
3bd3862
Fix .cancel() not behaving as expected
diegoimbert Oct 30, 2025
6913bca
Merge remote-tracking branch 'origin/main' into di/toast-alternatives…
diegoimbert Oct 30, 2025
c694660
fix nits
diegoimbert Oct 30, 2025
f5b88c1
audit logs nits
diegoimbert Oct 30, 2025
9de43bb
auditlog filter fix selects
diegoimbert Oct 30, 2025
9d9a91e
fix wrong number of jobs when switching perPage
diegoimbert Oct 30, 2025
0a2d350
default 1000
diegoimbert Oct 30, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@
"@redocly/json-to-json-schema": "^0.0.1",
"@scalar/openapi-parser": "^0.15.0",
"@tanstack/svelte-table": "npm:tanstack-table-8-svelte-5@^0.1",
"@tutorlatin/svelte-tiny-virtual-list": "^3.0.2",
"@tutorlatin/svelte-tiny-virtual-list": "^3.0.16",
"@windmill-labs/svelte-dnd-action": "^0.9.44",
"@xterm/addon-fit": "^0.10.0",
"@xyflow/svelte": "^1.0.0",
Expand Down
84 changes: 84 additions & 0 deletions frontend/src/lib/cancelable-promise-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { CancelablePromise } from './gen'

export namespace CancelablePromiseUtils {
export function then<T, U>(
promise: CancelablePromise<T>,
f: (value: T) => CancelablePromise<U>
): CancelablePromise<U> {
let promiseToBeCanceled: CancelablePromise<any> = promise
let p = new CancelablePromise<U>((resolve, reject) => {
promise
.then((value1) => {
let promise2 = f(value1)
promiseToBeCanceled = promise2
promise2.then((value2) => resolve(value2)).catch((err) => reject(err))
})
.catch((err) => reject(err))
})
p.cancel = () => promiseToBeCanceled.cancel()
return p
}

export function pure<T>(value: T): CancelablePromise<T> {
return new CancelablePromise((resolve) => resolve(value))
}

export function err<T>(error: any): CancelablePromise<T> {
return new CancelablePromise((_, reject) => reject(error))
}

export function map<T, U>(
promise: CancelablePromise<T>,
f: (value: T) => U
): CancelablePromise<U> {
return then(promise, (value) => pure(f(value)))
}

export function pipe<T>(
promise: CancelablePromise<T>,
f: (value: T) => void
): CancelablePromise<T> {
promise.then((value) => {
f(value)
})
return promise
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Critical: Potential memory leak and unhandled rejection

The pipe function doesn't handle promise rejections, which will cause unhandled promise rejection warnings. Additionally, it doesn't properly integrate with the cancellation chain.

Suggested fix:

export function pipe<T>(
	promise: CancelablePromise<T>,
	f: (value: T) => void
): CancelablePromise<T> {
	let p = new CancelablePromise<T>((resolve, reject) => {
		promise
			.then((value) => {
				f(value)
				resolve(value)
			})
			.catch((err) => reject(err))
	})
	p.cancel = () => promise.cancel()
	return p
}

Alternatively, if this function is not heavily used, consider using finallyDo instead, which already handles this pattern correctly.

}

export function catchErr<T, U>(
promise: CancelablePromise<T>,
f: (error: any) => CancelablePromise<U>
): CancelablePromise<T | U> {
let promiseToBeCanceled: CancelablePromise<any> = promise
let p = new CancelablePromise<T | U>((resolve, reject) => {
promise
.then((value) => resolve(value))
.catch((err) => {
let promise2 = f(err)
promiseToBeCanceled = promise2
return promise2.then((value2) => resolve(value2)).catch((err2) => reject(err2))
})
.catch((err) => reject(err))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Minor: Redundant catch block

The second .catch((err) => reject(err)) is redundant since all errors are already handled in the chain above. The first .catch block either resolves or rejects, so there's no error that would propagate to this second catch.

Consider removing this line for clarity.

})
p.cancel = () => promiseToBeCanceled.cancel()
return p
}

export function finallyDo<T>(promise: CancelablePromise<T>, f: () => void): CancelablePromise<T> {
promise = map(promise, (value) => (f(), value))
promise = catchErr(promise, (e) => (f(), err(e)))
return promise
}

// Calls onTimeout if the promise does not settle within timeoutMs milliseconds
export function onTimeout<T>(
promise: CancelablePromise<T>,
timeoutMs: number,
onTimeout: () => void
): CancelablePromise<T> {
let timeoutId: number | undefined = setTimeout(onTimeout, timeoutMs)
promise = finallyDo(promise, () => {
if (timeoutId !== undefined) clearTimeout(timeoutId)
})
return promise
}
}
17 changes: 10 additions & 7 deletions frontend/src/lib/components/RunChart.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
selectedIds?: string[]
canSelect?: boolean
lastFetchWentToEnd?: boolean
totalRowsFetched: number
onPointClicked: (ids: string[]) => void
onLoadExtra: () => void
onZoom: (zoom: { min: Date; max: Date }) => void
Expand All @@ -41,6 +42,7 @@
selectedIds = $bindable([]),
canSelect = true,
lastFetchWentToEnd = false,
totalRowsFetched,
onPointClicked,
onLoadExtra,
onZoom
Expand Down Expand Up @@ -301,13 +303,14 @@
<!-- {JSON.stringify(jobs?.map((x) => x.started_at))} -->
<div class="relative max-h-40">
{#if !lastFetchWentToEnd}
<div class="absolute top-[-28px] left-[220px]"
><Button size="xs" color="transparent" variant="contained" on:click={() => onLoadExtra()}
>Load more <Tooltip2
>There are more jobs to load but only the first 1000 were fetched</Tooltip2
></Button
></div
>
<div class="absolute top-[-28px] left-[220px]">
<Button size="xs" color="transparent" variant="contained" on:click={() => onLoadExtra()}>
Load more
<Tooltip2>
There are more jobs to load but only the first {totalRowsFetched} were fetched
</Tooltip2>
</Button>
</div>
{/if}
<Scatter {data} options={scatterOptions} />
</div>
Loading