windmill-components 1.542.5 → 1.550.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package/common.d.ts +4 -1
- package/package/components/AIAgentLogViewer.svelte +1 -1
- package/package/components/ArgEnum.svelte +14 -5
- package/package/components/ArgInput.svelte +23 -15
- package/package/components/ArgInput.svelte.d.ts +1 -1
- package/package/components/ChannelSelector.svelte +92 -18
- package/package/components/ChannelSelector.svelte.d.ts +2 -0
- package/package/components/ConnectionSection.svelte +12 -1
- package/package/components/Dev.svelte +18 -5
- package/package/components/Dev.svelte.d.ts +23 -1
- package/package/components/DisplayResult.svelte +36 -23
- package/package/components/DropdownV2.svelte +8 -2
- package/package/components/DropdownV2.svelte.d.ts +1 -0
- package/package/components/DynamicInput.svelte +10 -10
- package/package/components/EditableSchemaForm.svelte +21 -7
- package/package/components/ErrorOrRecoveryHandler.svelte +14 -20
- package/package/components/FlowHistoryJobPicker.svelte +3 -0
- package/package/components/FlowHistoryJobPicker.svelte.d.ts +1 -0
- package/package/components/FlowJobResult.svelte +5 -5
- package/package/components/FlowLogRow.svelte +2 -2
- package/package/components/FlowLogViewer.svelte +228 -57
- package/package/components/FlowLogViewer.svelte.d.ts +16 -5
- package/package/components/FlowLogViewerWrapper.svelte +56 -3
- package/package/components/FlowLogViewerWrapper.svelte.d.ts +4 -3
- package/package/components/FlowLoopIterationPreview.svelte +4 -4
- package/package/components/FlowMetadata.svelte +3 -4
- package/package/components/FlowMetadata.svelte.d.ts +4 -18
- package/package/components/FlowPreviewContent.svelte +9 -3
- package/package/components/FlowPreviewContent.svelte.d.ts +1 -1
- package/package/components/FlowStatusViewer.svelte +62 -59
- package/package/components/FlowStatusViewer.svelte.d.ts +2 -2
- package/package/components/FlowStatusViewerInner.svelte +186 -94
- package/package/components/FlowStatusViewerInner.svelte.d.ts +10 -3
- package/package/components/FlowTimeline.svelte +110 -131
- package/package/components/FlowTimeline.svelte.d.ts +13 -4
- package/package/components/FlowTimelineBar.svelte +227 -0
- package/package/components/FlowTimelineBar.svelte.d.ts +24 -0
- package/package/components/InputTransformForm.svelte +119 -3
- package/package/components/InputTransformForm.svelte.d.ts +3 -0
- package/package/components/InputTransformSchemaForm.svelte +5 -1
- package/package/components/InputTransformSchemaForm.svelte.d.ts +2 -0
- package/package/components/InstanceSetting.svelte +17 -42
- package/package/components/InstanceSettings.svelte +12 -21
- package/package/components/JobArgs.svelte +15 -16
- package/package/components/JobArgs.svelte.d.ts +4 -18
- package/package/components/JobLoader.svelte +23 -42
- package/package/components/JobLoader.svelte.d.ts +2 -0
- package/package/components/JobStatus.svelte +1 -1
- package/package/components/JobStatus.svelte.d.ts +4 -18
- package/package/components/ModulePreviewResultViewer.svelte +1 -7
- package/package/components/NextcloudSetting.svelte +6 -1
- package/package/components/Password.svelte +7 -11
- package/package/components/Password.svelte.d.ts +5 -20
- package/package/components/PasswordArgInput.svelte +35 -15
- package/package/components/PasswordArgInput.svelte.d.ts +4 -18
- package/package/components/QueuePosition.svelte +6 -2
- package/package/components/RunForm.svelte +5 -14
- package/package/components/S3ArrayHelperButton.svelte +12 -0
- package/package/components/S3ArrayHelperButton.svelte.d.ts +8 -0
- package/package/components/ScriptEditor.svelte +5 -6
- package/package/components/StringTypeNarrowing.svelte +39 -24
- package/package/components/StringTypeNarrowing.svelte.d.ts +1 -1
- package/package/components/TeamSelector.svelte +83 -37
- package/package/components/TeamSelector.svelte.d.ts +0 -1
- package/package/components/apps/components/buttons/AppButton.svelte +11 -1
- package/package/components/apps/components/display/table/AppAggridInfiniteTable.svelte +13 -4
- package/package/components/apps/components/display/table/SyncColumnDefs.svelte +2 -2
- package/package/components/apps/components/display/table/utils.js +1 -1
- package/package/components/apps/components/helpers/RefreshButton.svelte +5 -1
- package/package/components/apps/components/helpers/RunnableComponent.svelte +0 -2
- package/package/components/apps/components/helpers/RunnableWrapper.svelte.d.ts +1 -0
- package/package/components/apps/components/layout/AppTabs.svelte +116 -71
- package/package/components/apps/components/layout/AppTabs.svelte.d.ts +1 -0
- package/package/components/apps/editor/component/ComponentInner.svelte +1 -0
- package/package/components/apps/editor/component/components.d.ts +16 -1
- package/package/components/apps/editor/component/components.js +22 -2
- package/package/components/apps/editor/settingsPanel/ComponentPanel.svelte +2 -0
- package/package/components/apps/editor/settingsPanel/GridTab.svelte +19 -1
- package/package/components/apps/editor/settingsPanel/GridTab.svelte.d.ts +3 -1
- package/package/components/apps/editor/settingsPanel/GridTabHidden.svelte +52 -0
- package/package/components/apps/editor/settingsPanel/GridTabHidden.svelte.d.ts +9 -0
- package/package/components/auditLogs/AuditLogsFilters.svelte +6 -0
- package/package/components/auditLogs/AuditLogsTable.svelte +17 -7
- package/package/components/auditLogs/AuditLogsTable.svelte.d.ts +1 -0
- package/package/components/common/CloseButton.svelte +2 -2
- package/package/components/common/CloseButton.svelte.d.ts +1 -0
- package/package/components/common/layout/List.svelte +3 -7
- package/package/components/common/layout/List.svelte.d.ts +7 -29
- package/package/components/common/popup/PopupV2.svelte +8 -25
- package/package/components/common/popup/PopupV2.svelte.d.ts +4 -2
- package/package/components/common/table/ScriptRow.svelte +22 -2
- package/package/components/copilot/FlowCopilotInputsModal.svelte +26 -23
- package/package/components/copilot/chat/AIChatManager.svelte.js +3 -2
- package/package/components/copilot/chat/ProviderModelSelector.svelte +1 -1
- package/package/components/copilot/chat/flow/FlowAIChat.svelte +4 -2
- package/package/components/copilot/chat/script/core.d.ts +4 -4
- package/package/components/copilot/chat/script/core.js +93 -34
- package/package/components/copilot/lib.d.ts +1 -0
- package/package/components/copilot/lib.js +6 -3
- package/package/components/custom_ui.d.ts +1 -0
- package/package/components/flows/FlowProgressBar.svelte +16 -16
- package/package/components/flows/FlowProgressBar.svelte.d.ts +7 -22
- package/package/components/flows/content/FlowInputsQuick.svelte +3 -2
- package/package/components/flows/content/FlowInputsQuick.svelte.d.ts +1 -0
- package/package/components/flows/content/FlowModuleComponent.svelte +24 -1
- package/package/components/flows/flowInfers.js +34 -8
- package/package/components/flows/flowStore.d.ts +4 -1
- package/package/components/flows/map/FlowJobsMenu.svelte +3 -3
- package/package/components/flows/map/FlowJobsMenu.svelte.d.ts +1 -1
- package/package/components/flows/map/InsertModuleButton.svelte +4 -14
- package/package/components/flows/map/InsertModuleButton.svelte.d.ts +0 -1
- package/package/components/flows/map/InsertModuleInner.svelte +17 -20
- package/package/components/flows/map/MapItem.svelte +1 -1
- package/package/components/flows/pickers/PickHubScriptQuick.svelte +38 -52
- package/package/components/flows/pickers/PickHubScriptQuick.svelte.d.ts +1 -0
- package/package/components/flows/pickers/WorkspaceScriptPickerQuick.svelte +27 -15
- package/package/components/flows/pickers/WorkspaceScriptPickerQuick.svelte.d.ts +1 -0
- package/package/components/flows/propPicker/OutputPicker.svelte +2 -0
- package/package/components/git_sync/DetectionFlow.svelte +33 -44
- package/package/components/git_sync/DetectionFlow.svelte.d.ts +1 -0
- package/package/components/git_sync/GitSyncContext.svelte.d.ts +22 -0
- package/package/components/git_sync/GitSyncContext.svelte.js +145 -5
- package/package/components/git_sync/GitSyncModeDisplay.svelte +14 -0
- package/package/components/git_sync/GitSyncModeDisplay.svelte.d.ts +9 -0
- package/package/components/git_sync/GitSyncRepositoryCard.svelte +365 -253
- package/package/components/git_sync/GitSyncRepositoryCard.svelte.d.ts +10 -1
- package/package/components/git_sync/GitSyncSection.svelte +134 -14
- package/package/components/git_sync/PullWorkspaceModal.svelte +24 -32
- package/package/components/git_sync/PushWorkspaceModal.svelte +24 -32
- package/package/components/graph/model.d.ts +5 -5
- package/package/components/graph/renderers/edges/EmptyEdge.svelte +3 -10
- package/package/components/graph/renderers/edges/EmptyEdge.svelte.d.ts +4 -18
- package/package/components/graph/renderers/nodes/AIToolNode.svelte +2 -2
- package/package/components/graph/renderers/nodes/NewAIToolNode.svelte +5 -10
- package/package/components/home/ItemsList.svelte +1 -1
- package/package/components/jobs/JobProgressBar.svelte +27 -21
- package/package/components/jobs/JobProgressBar.svelte.d.ts +9 -24
- package/package/components/meltComponents/MenuSingleItem.svelte +3 -8
- package/package/components/meltComponents/MenuSingleItem.svelte.d.ts +0 -3
- package/package/components/meltComponents/Popover.svelte +3 -2
- package/package/components/meltComponents/Popover.svelte.d.ts +1 -0
- package/package/components/meltComponents/Tooltip.svelte +1 -1
- package/package/components/progressBar/ProgressBar.svelte +39 -53
- package/package/components/progressBar/ProgressBar.svelte.d.ts +11 -26
- package/package/components/runs/JobsLoader.svelte +1 -1
- package/package/components/runs/NoWorkerWithTagWarning.svelte +3 -3
- package/package/components/runs/NoWorkerWithTagWarning.svelte.d.ts +1 -1
- package/package/components/schema/AddPropertyV2.svelte +7 -4
- package/package/components/schema/PropertyEditor.svelte.d.ts +1 -1
- package/package/components/select/MultiSelect.svelte +2 -2
- package/package/components/select/MultiSelect.svelte.d.ts +1 -0
- package/package/components/settings/WorkspaceUserSettings.svelte +92 -1
- package/package/components/sidebar/MenuLink.svelte +2 -1
- package/package/components/sidebar/MenuLink.svelte.d.ts +1 -0
- package/package/components/sidebar/SidebarContent.svelte +27 -27
- package/package/components/table/Cell.svelte +7 -14
- package/package/components/table/Cell.svelte.d.ts +13 -35
- package/package/components/triggers/AddTriggersButton.svelte +1 -0
- package/package/components/triggers/gcp/GcpTriggerEditorConfigSection.svelte +1 -1
- package/package/components/triggers/gcp/GcpTriggerEditorConfigSection.svelte.d.ts +2 -1
- package/package/components/triggers/gcp/GcpTriggerEditorInner.svelte +28 -5
- package/package/components/triggers/gcp/utils.js +1 -0
- package/package/components/triggers/schedules/ScheduleEditorInner.svelte +1 -0
- package/package/components/triggers/webhook/WebhooksConfigSection.svelte +143 -63
- package/package/components/triggers/websocket/WebsocketTriggerEditorInner.svelte +22 -0
- package/package/components/triggers/websocket/utils.js +1 -0
- package/package/components/workspaceSettings/AISettings.svelte +8 -2
- package/package/components/workspaceSettings/AISettings.svelte.d.ts +2 -1
- package/package/components/workspaceSettings/ModelTokenLimits.svelte +165 -0
- package/package/components/workspaceSettings/ModelTokenLimits.svelte.d.ts +8 -0
- package/package/components/workspaceSettings/StorageSettings.svelte +123 -51
- package/package/gen/core/OpenAPI.js +1 -1
- package/package/gen/schemas.gen.d.ts +141 -16
- package/package/gen/schemas.gen.js +144 -16
- package/package/gen/services.gen.d.ts +62 -42
- package/package/gen/services.gen.js +131 -82
- package/package/gen/types.gen.d.ts +218 -144
- package/package/hubPaths.json +2 -1
- package/package/services/JobManager.js +10 -7
- package/package/stores.d.ts +1 -0
- package/package/stores.js +6 -3
- package/package/timelineCompute.svelte.d.ts +21 -0
- package/package/timelineCompute.svelte.js +113 -0
- package/package/utils.d.ts +15 -8
- package/package/utils.js +62 -12
- package/package/workspace_settings.d.ts +13 -8
- package/package/workspace_settings.js +46 -11
- package/package.json +2 -2
|
@@ -1,104 +1,57 @@
|
|
|
1
|
-
<script lang="ts">import {
|
|
2
|
-
import { onDestroy, untrack } from 'svelte';
|
|
3
|
-
import { getDbClockNow } from '../forLater';
|
|
1
|
+
<script lang="ts">import { displayDate, msToSec } from '../utils';
|
|
4
2
|
import { Loader2 } from 'lucide-svelte';
|
|
5
3
|
import TimelineBar from './TimelineBar.svelte';
|
|
6
4
|
import WaitTimeWarning from './common/waitTimeWarning/WaitTimeWarning.svelte';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
let {
|
|
5
|
+
import { TimelineCompute } from '../timelineCompute.svelte';
|
|
6
|
+
import { onMount, untrack } from 'svelte';
|
|
7
|
+
import OnChange from './common/OnChange.svelte';
|
|
8
|
+
import VirtualList from '@tutorlatin/svelte-tiny-virtual-list';
|
|
9
|
+
import FlowJobsMenu from './flows/map/FlowJobsMenu.svelte';
|
|
10
|
+
let { selfWaitTime = undefined, aggregateWaitTime = undefined, flowModules, durationStatuses, flowDone = false, localModuleStates, onSelectedIteration } = $props();
|
|
11
|
+
let timelineCompute = $state(undefined);
|
|
12
|
+
const flowModulesIds = $derived(flowModules.map(({ id }) => id));
|
|
13
|
+
// Initialize timeline compute when we have duration statuses
|
|
14
|
+
onMount(() => {
|
|
15
|
+
timelineCompute = new TimelineCompute(flowModulesIds, durationStatuses, flowDone);
|
|
16
|
+
return () => {
|
|
17
|
+
timelineCompute?.destroy();
|
|
18
|
+
};
|
|
19
|
+
});
|
|
13
20
|
$effect(() => {
|
|
14
|
-
|
|
15
|
-
|
|
21
|
+
flowDone;
|
|
22
|
+
untrack(() => {
|
|
23
|
+
timelineCompute?.setFlowDone(flowDone);
|
|
24
|
+
});
|
|
16
25
|
});
|
|
26
|
+
// Derived timeline values
|
|
27
|
+
const min = $derived(timelineCompute?.min ?? undefined);
|
|
28
|
+
const max = $derived(timelineCompute?.max ?? undefined);
|
|
29
|
+
const total = $derived(timelineCompute?.total ?? undefined);
|
|
30
|
+
const items = $derived(timelineCompute?.items ?? undefined);
|
|
31
|
+
const now = $derived(timelineCompute?.now ?? Date.now());
|
|
17
32
|
export function reset() {
|
|
18
|
-
|
|
19
|
-
max = undefined;
|
|
20
|
-
items = computeItems(durationStatuses);
|
|
33
|
+
timelineCompute?.reset();
|
|
21
34
|
}
|
|
22
|
-
|
|
23
|
-
let nmin = undefined;
|
|
24
|
-
let nmax = undefined;
|
|
25
|
-
let isStillRunning = false;
|
|
26
|
-
let cnt = 0;
|
|
27
|
-
let nitems = {};
|
|
28
|
-
Object.entries(durationStatuses).forEach(([k, o]) => {
|
|
29
|
-
Object.values(o.byJob).forEach((v) => {
|
|
30
|
-
cnt++;
|
|
31
|
-
if (v.started_at) {
|
|
32
|
-
if (!nmin) {
|
|
33
|
-
nmin = v.started_at;
|
|
34
|
-
}
|
|
35
|
-
else {
|
|
36
|
-
nmin = Math.min(nmin, v.started_at);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
if (!flowDone && v.duration_ms == undefined) {
|
|
40
|
-
isStillRunning = true;
|
|
41
|
-
}
|
|
42
|
-
if (!isStillRunning) {
|
|
43
|
-
if (v.started_at && v.duration_ms != undefined) {
|
|
44
|
-
let lmax = v.started_at + v.duration_ms;
|
|
45
|
-
if (!nmax) {
|
|
46
|
-
nmax = lmax;
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
49
|
-
nmax = Math.max(nmax, lmax);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
let arr = Object.entries(o.byJob).map(([k, v]) => ({ ...v, id: k }));
|
|
55
|
-
arr.sort((x, y) => {
|
|
56
|
-
if (!x.started_at) {
|
|
57
|
-
return -1;
|
|
58
|
-
}
|
|
59
|
-
else if (!y.started_at) {
|
|
60
|
-
return 1;
|
|
61
|
-
}
|
|
62
|
-
else {
|
|
63
|
-
return x.started_at - y.started_at;
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
nitems[k] = arr;
|
|
67
|
-
});
|
|
68
|
-
items = nitems;
|
|
69
|
-
min = nmin;
|
|
70
|
-
max = isStillRunning || (cnt < flowModules.length && !flowDone) ? undefined : nmax;
|
|
71
|
-
if (max && min) {
|
|
72
|
-
total = max - min;
|
|
73
|
-
total = Math.max(total, 2000);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
let now = $state(getDbClockNow().getTime());
|
|
77
|
-
let interval = setInterval((x) => {
|
|
78
|
-
if (!max) {
|
|
79
|
-
now = getDbClockNow().getTime();
|
|
80
|
-
}
|
|
81
|
-
if (min && (!max || total == undefined)) {
|
|
82
|
-
total = max ? max - min : Math.max(now - min, 2000);
|
|
83
|
-
}
|
|
84
|
-
}, 30);
|
|
85
|
-
onDestroy(() => {
|
|
86
|
-
interval && clearInterval(interval);
|
|
87
|
-
clearDebounce();
|
|
88
|
-
});
|
|
35
|
+
const barHeight = 32;
|
|
89
36
|
</script>
|
|
90
37
|
|
|
38
|
+
<OnChange
|
|
39
|
+
key={durationStatuses}
|
|
40
|
+
onChange={() => {
|
|
41
|
+
timelineCompute?.updateInputs(flowModulesIds, durationStatuses, flowDone)
|
|
42
|
+
}}
|
|
43
|
+
/>
|
|
91
44
|
{#if items}
|
|
92
45
|
<div class="divide-y border-b">
|
|
93
46
|
<div class="px-2 py-2 grid grid-cols-12 w-full"
|
|
94
47
|
><div></div>
|
|
95
48
|
<div class="col-span-11 pt-1 px-2 flex text-2xs text-secondary justify-between"
|
|
96
49
|
><div>{min ? displayDate(new Date(min), true) : ''}</div>{#if max && min}<div
|
|
97
|
-
class="hidden lg:block">{msToSec(max - min)}s</div
|
|
50
|
+
class="hidden lg:block">{msToSec(max - min, 1)}s</div
|
|
98
51
|
>
|
|
99
52
|
{/if}<div class="flex gap-1 items-center font-mono"
|
|
100
53
|
>{max ? displayDate(new Date(max), true) : ''}{#if !max && min}{#if now}
|
|
101
|
-
{msToSec(now - min,
|
|
54
|
+
{msToSec(now - min, 1)}s
|
|
102
55
|
{/if}<Loader2 size={14} class="animate-spin" />{/if}</div
|
|
103
56
|
></div
|
|
104
57
|
>
|
|
@@ -126,61 +79,87 @@ onDestroy(() => {
|
|
|
126
79
|
/>
|
|
127
80
|
</div>
|
|
128
81
|
{/if}
|
|
129
|
-
{#each
|
|
130
|
-
{@const
|
|
131
|
-
<div class="
|
|
132
|
-
{#if iterationFrom > 0}
|
|
133
|
-
<div class="w-full flex flex-row-reverse sticky top-0">
|
|
134
|
-
<button
|
|
135
|
-
class="!text-secondary underline mr-2 text-2xs text-right whitespace-nowrap"
|
|
136
|
-
onclick={() => {
|
|
137
|
-
decreaseIterationFrom?.(k, 20)
|
|
138
|
-
}}
|
|
139
|
-
>Viewing iterations {iterationFrom} to {globalIterationBounds[buildSubflowKey(k)]
|
|
140
|
-
?.iteration_total}. Load more
|
|
141
|
-
</button>
|
|
142
|
-
</div>
|
|
143
|
-
{/if}
|
|
144
|
-
|
|
82
|
+
{#each flowModules as { id: k, type: typ } (k)}
|
|
83
|
+
{@const subItems = items?.[k]?.filter((x) => x.created_at && x.started_at)}
|
|
84
|
+
<div class="shadow-inner dark:shadow-gray-700 relative">
|
|
145
85
|
<div class="px-2 py-2 grid grid-cols-6 w-full">
|
|
146
|
-
<div class="truncate"
|
|
147
|
-
|
|
86
|
+
<div class="truncate"
|
|
87
|
+
>{k.startsWith('subflow:') ? k.substring(8) : k}
|
|
88
|
+
{#if localModuleStates[k]?.selectedForloop && (typ == 'forloopflow' || typ == 'whileloopflow')}
|
|
89
|
+
<span class="text-xs font-mono font-medium inline-flex items-center -my-2">
|
|
90
|
+
<button onclick={(e) => e.stopPropagation()}>
|
|
91
|
+
<FlowJobsMenu
|
|
92
|
+
moduleId={k}
|
|
93
|
+
id={k}
|
|
94
|
+
{onSelectedIteration}
|
|
95
|
+
flowJobsSuccess={localModuleStates[k]?.flow_jobs_success}
|
|
96
|
+
flowJobs={localModuleStates[k]?.flow_jobs}
|
|
97
|
+
selected={localModuleStates[k]?.selectedForloopIndex ?? 0}
|
|
98
|
+
selectedManually={localModuleStates[k]?.selectedForLoopSetManually ?? false}
|
|
99
|
+
showIcon={false}
|
|
100
|
+
/>
|
|
101
|
+
</button>
|
|
102
|
+
</span>
|
|
103
|
+
{/if}
|
|
104
|
+
</div>
|
|
105
|
+
<div class="col-span-5 flex">
|
|
106
|
+
{#if subItems?.length > 1}
|
|
107
|
+
<div class="text-xs text-secondary absolute top-1 right-2">
|
|
108
|
+
{subItems?.length} jobs
|
|
109
|
+
</div>
|
|
110
|
+
{/if}
|
|
148
111
|
{#if min && total}
|
|
149
|
-
<
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
<
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
/>
|
|
169
|
-
{#if b.started_at}
|
|
112
|
+
<VirtualList
|
|
113
|
+
width="100%"
|
|
114
|
+
height={Math.min(400, (subItems?.length ?? 0) * barHeight)}
|
|
115
|
+
itemCount={subItems?.length ?? 0}
|
|
116
|
+
itemSize={barHeight}
|
|
117
|
+
getKey={(index) => subItems?.[index]?.id}
|
|
118
|
+
>
|
|
119
|
+
{#snippet item({ index, style })}
|
|
120
|
+
{@const b = subItems?.[index]}
|
|
121
|
+
{#if b?.created_at}
|
|
122
|
+
<!-- <div class="text-xs text-secondary">{JSON.stringify(b)}</div> -->
|
|
123
|
+
{@const waitingLen = b?.created_at
|
|
124
|
+
? b.started_at
|
|
125
|
+
? b.started_at - b?.created_at
|
|
126
|
+
: b.duration_ms
|
|
127
|
+
? 0
|
|
128
|
+
: now - b?.created_at
|
|
129
|
+
: 0}
|
|
130
|
+
<div class="flex w-full p-1 pb-2 pl-12" {style}>
|
|
170
131
|
<TimelineBar
|
|
171
|
-
position=
|
|
132
|
+
position="left"
|
|
172
133
|
id={b?.id}
|
|
173
134
|
{total}
|
|
174
135
|
{min}
|
|
175
|
-
|
|
176
|
-
started_at={b.
|
|
177
|
-
len={
|
|
178
|
-
running={b?.
|
|
136
|
+
gray
|
|
137
|
+
started_at={b.created_at}
|
|
138
|
+
len={waitingLen < 100 ? 0 : waitingLen - 100}
|
|
139
|
+
running={b?.started_at == undefined}
|
|
179
140
|
/>
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
141
|
+
{#if b.started_at}
|
|
142
|
+
<TimelineBar
|
|
143
|
+
position={waitingLen < 100 ? 'center' : 'right'}
|
|
144
|
+
id={b?.id}
|
|
145
|
+
{total}
|
|
146
|
+
{min}
|
|
147
|
+
concat
|
|
148
|
+
started_at={b.started_at}
|
|
149
|
+
len={b.started_at ? (b?.duration_ms ?? now - b?.started_at) : 0}
|
|
150
|
+
running={b?.duration_ms == undefined}
|
|
151
|
+
/>
|
|
152
|
+
{/if}
|
|
153
|
+
</div>
|
|
154
|
+
{:else}
|
|
155
|
+
<div class="flex w-full p-1 pb-2 pl-12">
|
|
156
|
+
<div class="text-xs text-secondary">
|
|
157
|
+
<!-- Waiting for executor/Suspend {JSON.stringify(b)} -->
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
{/if}
|
|
161
|
+
{/snippet}
|
|
162
|
+
</VirtualList>
|
|
184
163
|
{/if}</div
|
|
185
164
|
></div
|
|
186
165
|
>
|
|
@@ -1,8 +1,19 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { GraphModuleState } from './graph';
|
|
2
|
+
import type { FlowModuleForTimeline } from './FlowStatusViewerInner.svelte';
|
|
2
3
|
interface Props {
|
|
3
4
|
selfWaitTime?: number | undefined;
|
|
4
5
|
aggregateWaitTime?: number | undefined;
|
|
5
|
-
flowModules:
|
|
6
|
+
flowModules: FlowModuleForTimeline[];
|
|
7
|
+
localModuleStates: Record<string, GraphModuleState>;
|
|
8
|
+
onSelectedIteration?: (detail: {
|
|
9
|
+
id: string;
|
|
10
|
+
index: number;
|
|
11
|
+
manuallySet: true;
|
|
12
|
+
moduleId: string;
|
|
13
|
+
} | {
|
|
14
|
+
manuallySet: false;
|
|
15
|
+
moduleId: string;
|
|
16
|
+
}) => Promise<void>;
|
|
6
17
|
durationStatuses: Record<string, {
|
|
7
18
|
byJob: Record<string, {
|
|
8
19
|
created_at?: number;
|
|
@@ -11,9 +22,7 @@ interface Props {
|
|
|
11
22
|
}>;
|
|
12
23
|
}>;
|
|
13
24
|
flowDone?: boolean;
|
|
14
|
-
decreaseIterationFrom?: (key: string, amount: number) => void;
|
|
15
25
|
buildSubflowKey: (key: string) => string;
|
|
16
|
-
globalIterationBounds: Record<string, GlobalIterationBounds>;
|
|
17
26
|
}
|
|
18
27
|
declare const FlowTimeline: import("svelte").Component<Props, {
|
|
19
28
|
reset: () => void;
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
<script lang="ts">import { msToReadableTime, msToReadableTimeShort } from '../utils';
|
|
2
|
+
import { ZoomIn, ZoomOut } from 'lucide-svelte';
|
|
3
|
+
import { twMerge } from 'tailwind-merge';
|
|
4
|
+
import { Tooltip } from './meltComponents';
|
|
5
|
+
let { total, min, items, selectedIndex, now, timelinelWidth, showZoomButtons = false, onZoom, zoom = 'in', onSelectIteration, idToIterationIndex, showIterations, isJobFailure } = $props();
|
|
6
|
+
function getLength(item) {
|
|
7
|
+
if (!item?.started_at)
|
|
8
|
+
return 0;
|
|
9
|
+
return item.duration_ms ?? now - item.started_at;
|
|
10
|
+
}
|
|
11
|
+
function isRunning(item) {
|
|
12
|
+
return item.started_at !== undefined && item.duration_ms === undefined;
|
|
13
|
+
}
|
|
14
|
+
const filteredItems = $derived(showIterations ? items.filter((item) => showIterations.includes(item.id)) : items);
|
|
15
|
+
let selectedItem = $derived(selectedIndex && selectedIndex >= 0 ? filteredItems[selectedIndex] : filteredItems[0]);
|
|
16
|
+
let startItem = $derived(showIterations ? filteredItems[0] : selectedItem);
|
|
17
|
+
// Calculate total execution time for multiple filteredItems
|
|
18
|
+
function calculateTotalExecutionTime() {
|
|
19
|
+
let earliestStart;
|
|
20
|
+
let latestEnd = 0;
|
|
21
|
+
for (const item of filteredItems) {
|
|
22
|
+
if (item.started_at) {
|
|
23
|
+
// Track earliest start
|
|
24
|
+
if (!earliestStart || item.started_at < earliestStart) {
|
|
25
|
+
earliestStart = item.started_at;
|
|
26
|
+
}
|
|
27
|
+
// Track latest end
|
|
28
|
+
const itemEnd = item.duration_ms ? item.started_at + item.duration_ms : now;
|
|
29
|
+
latestEnd = Math.max(latestEnd, itemEnd);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return earliestStart ? latestEnd - earliestStart : 0;
|
|
33
|
+
}
|
|
34
|
+
let selectedLen = $derived(
|
|
35
|
+
// If selectedIteration is set, it means we are in a loop and we are selecting an iteration
|
|
36
|
+
showIterations ? calculateTotalExecutionTime() : getLength(selectedItem));
|
|
37
|
+
const waitingLen = $derived(startItem?.created_at
|
|
38
|
+
? startItem.started_at
|
|
39
|
+
? startItem.started_at - startItem.created_at
|
|
40
|
+
: startItem.duration_ms
|
|
41
|
+
? 0
|
|
42
|
+
: now - startItem.created_at
|
|
43
|
+
: 0);
|
|
44
|
+
function calculateItemPosition(item) {
|
|
45
|
+
if (!item.started_at || !min)
|
|
46
|
+
return { left: 0, width: 0 };
|
|
47
|
+
const startOffset = item.started_at - min;
|
|
48
|
+
const duration = getLength(item);
|
|
49
|
+
const leftPercent = (startOffset / total) * 100;
|
|
50
|
+
const widthPercent = (duration / total) * 100;
|
|
51
|
+
return { left: leftPercent, width: widthPercent };
|
|
52
|
+
}
|
|
53
|
+
// More efficient version using sweep line algorithm for computing all overlaps at once
|
|
54
|
+
function computeAllOverlaps(items) {
|
|
55
|
+
const overlapCounts = new Map();
|
|
56
|
+
const events = [];
|
|
57
|
+
for (const item of items) {
|
|
58
|
+
if (!item.started_at)
|
|
59
|
+
continue;
|
|
60
|
+
const endTime = item.duration_ms ? item.started_at + item.duration_ms : now;
|
|
61
|
+
events.push({ time: item.started_at, type: 'start', itemId: item.id });
|
|
62
|
+
events.push({ time: endTime, type: 'end', itemId: item.id });
|
|
63
|
+
overlapCounts.set(item.id, 0);
|
|
64
|
+
}
|
|
65
|
+
// Sort events by time, with end events before start events at the same time
|
|
66
|
+
events.sort((a, b) => {
|
|
67
|
+
if (a.time !== b.time)
|
|
68
|
+
return a.time - b.time;
|
|
69
|
+
return a.type === 'end' ? -1 : 1;
|
|
70
|
+
});
|
|
71
|
+
// Sweep through events
|
|
72
|
+
const activeItems = new Set();
|
|
73
|
+
for (const event of events) {
|
|
74
|
+
if (event.type === 'start') {
|
|
75
|
+
// Count current active items as overlaps for this item
|
|
76
|
+
overlapCounts.set(event.itemId, activeItems.size);
|
|
77
|
+
// Update overlap counts for all currently active items
|
|
78
|
+
for (const activeId of activeItems) {
|
|
79
|
+
overlapCounts.set(activeId, overlapCounts.get(activeId) + 1);
|
|
80
|
+
}
|
|
81
|
+
activeItems.add(event.itemId);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
activeItems.delete(event.itemId);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return Object.fromEntries(overlapCounts.entries());
|
|
88
|
+
}
|
|
89
|
+
// At component level, compute once when items change
|
|
90
|
+
const allOverlaps = $derived(computeAllOverlaps(filteredItems));
|
|
91
|
+
const maximumOverlaps = $derived(Math.max(...Object.values(allOverlaps)));
|
|
92
|
+
const opacity = $derived(Math.max(0.02, 1 / maximumOverlaps));
|
|
93
|
+
// Then in your template, use:
|
|
94
|
+
// allOverlaps.get(item.id) ?? 0
|
|
95
|
+
</script>
|
|
96
|
+
|
|
97
|
+
{#if min && filteredItems.length > 0 && startItem?.started_at}
|
|
98
|
+
<div
|
|
99
|
+
class="flex items-center gap-2 ml-auto min-w-96 max-w-[1000px] h-4 group"
|
|
100
|
+
style="width: {timelinelWidth}px"
|
|
101
|
+
>
|
|
102
|
+
{#if showZoomButtons}
|
|
103
|
+
<div class="w-24 flex items-center justify-end">
|
|
104
|
+
<button
|
|
105
|
+
onclick={(e) => {
|
|
106
|
+
e.stopPropagation()
|
|
107
|
+
onZoom?.()
|
|
108
|
+
}}
|
|
109
|
+
class="hover:text-primary hover:bg-surface p-1 -my-1 rounded-md"
|
|
110
|
+
>
|
|
111
|
+
{#if zoom === 'in'}
|
|
112
|
+
<ZoomOut size={12} />
|
|
113
|
+
{:else}
|
|
114
|
+
<ZoomIn size={12} />
|
|
115
|
+
{/if}
|
|
116
|
+
</button>
|
|
117
|
+
</div>
|
|
118
|
+
{:else}
|
|
119
|
+
<div class="w-24"></div>
|
|
120
|
+
{/if}
|
|
121
|
+
<div
|
|
122
|
+
class="flex-1 h-1 bg-gray-300 dark:bg-gray-800 rounded-sm overflow-hidden group-hover:h-full transition-all duration-100 relative"
|
|
123
|
+
>
|
|
124
|
+
{#if waitingLen > 100 && startItem.created_at}
|
|
125
|
+
<div
|
|
126
|
+
style="width: {((startItem.created_at - min) / total) * 100}%"
|
|
127
|
+
class="h-full absolute left-0 top-0"
|
|
128
|
+
>
|
|
129
|
+
</div>
|
|
130
|
+
<div
|
|
131
|
+
style="left: {((startItem.created_at - min) / total) * 100}%; width: {(waitingLen /
|
|
132
|
+
total) *
|
|
133
|
+
100}%"
|
|
134
|
+
class="h-full absolute top-0 bg-gray-300 dark:bg-gray-600"
|
|
135
|
+
title={msToReadableTime(waitingLen, 1)}
|
|
136
|
+
>
|
|
137
|
+
</div>
|
|
138
|
+
{:else if startItem?.started_at}
|
|
139
|
+
<div
|
|
140
|
+
style="width: {((startItem.started_at - min) / total) * 100}%"
|
|
141
|
+
class="h-full absolute left-0 top-0"
|
|
142
|
+
></div>
|
|
143
|
+
{/if}
|
|
144
|
+
|
|
145
|
+
{#if showIterations}
|
|
146
|
+
<!-- All iterations with absolute positioning -->
|
|
147
|
+
{#each filteredItems as item, i}
|
|
148
|
+
{#if item.started_at}
|
|
149
|
+
{@const position = calculateItemPosition(item)}
|
|
150
|
+
<!-- {overlapCount}
|
|
151
|
+
{opacity} -->
|
|
152
|
+
<Tooltip
|
|
153
|
+
style="left: {position.left}%; width: {position.width}%"
|
|
154
|
+
class="h-full absolute top-0"
|
|
155
|
+
openDelay={100}
|
|
156
|
+
>
|
|
157
|
+
<!-- svelte-ignore a11y_consider_explicit_label -->
|
|
158
|
+
<div class="relative w-full h-full">
|
|
159
|
+
<button
|
|
160
|
+
class={twMerge(
|
|
161
|
+
'w-full h-full hover:outline outline-1 outline-blue-800 dark:outline-blue-300 -outline-offset-1 rounded-sm block transition-opacity duration-200',
|
|
162
|
+
isRunning(item)
|
|
163
|
+
? 'bg-blue-400'
|
|
164
|
+
: isJobFailure?.(item.id)
|
|
165
|
+
? ' bg-red-500'
|
|
166
|
+
: ' bg-blue-500',
|
|
167
|
+
i > 0 ? 'border-l border-gray-300 dark:border-gray-800 ' : '',
|
|
168
|
+
i === selectedIndex ? 'outline' : ''
|
|
169
|
+
)}
|
|
170
|
+
style="opacity: {opacity}"
|
|
171
|
+
onclick={(e) => {
|
|
172
|
+
e.stopPropagation()
|
|
173
|
+
onSelectIteration?.(item.id)
|
|
174
|
+
}}
|
|
175
|
+
>
|
|
176
|
+
</button>
|
|
177
|
+
</div>
|
|
178
|
+
{#snippet text()}
|
|
179
|
+
{`#${(idToIterationIndex?.(item.id) ?? 0) + 1}`}
|
|
180
|
+
<br />
|
|
181
|
+
{msToReadableTime(getLength(item), 1)}
|
|
182
|
+
{#if opacity < 1}
|
|
183
|
+
<br />
|
|
184
|
+
<span class="text-xs opacity-75">Overlapping</span>
|
|
185
|
+
{/if}
|
|
186
|
+
{/snippet}
|
|
187
|
+
</Tooltip>
|
|
188
|
+
{/if}
|
|
189
|
+
{/each}
|
|
190
|
+
{:else}
|
|
191
|
+
<!-- Single item case or inside a loop -->
|
|
192
|
+
{#if selectedItem?.started_at}
|
|
193
|
+
{@const position = calculateItemPosition(selectedItem)}
|
|
194
|
+
<Tooltip
|
|
195
|
+
class="h-full absolute top-0"
|
|
196
|
+
style="left: {position.left}%; width: {position.width}%"
|
|
197
|
+
openDelay={100}
|
|
198
|
+
>
|
|
199
|
+
<!-- svelte-ignore a11y_consider_explicit_label -->
|
|
200
|
+
<button
|
|
201
|
+
class={twMerge(
|
|
202
|
+
'block w-full h-full hover:outline outline-1 outline-white -outline-offset-1 rounded-sm',
|
|
203
|
+
isRunning(selectedItem)
|
|
204
|
+
? 'bg-blue-400'
|
|
205
|
+
: isJobFailure?.(selectedItem.id)
|
|
206
|
+
? ' bg-red-500'
|
|
207
|
+
: ' bg-blue-500'
|
|
208
|
+
)}
|
|
209
|
+
onclick={(e) => {
|
|
210
|
+
e.stopPropagation()
|
|
211
|
+
}}
|
|
212
|
+
></button>
|
|
213
|
+
{#snippet text()}
|
|
214
|
+
{msToReadableTime(selectedLen, 1)}
|
|
215
|
+
{/snippet}
|
|
216
|
+
</Tooltip>
|
|
217
|
+
{/if}
|
|
218
|
+
{/if}
|
|
219
|
+
</div>
|
|
220
|
+
{#if selectedLen > 0}
|
|
221
|
+
<span
|
|
222
|
+
class="text-2xs text-tertiary font-mono font-normal w-10 truncate"
|
|
223
|
+
title={msToReadableTime(selectedLen, 1)}>{msToReadableTimeShort(selectedLen, 1)}</span
|
|
224
|
+
>
|
|
225
|
+
{/if}
|
|
226
|
+
</div>
|
|
227
|
+
{/if}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
interface TimelineItem {
|
|
2
|
+
created_at?: number;
|
|
3
|
+
started_at?: number;
|
|
4
|
+
duration_ms?: number;
|
|
5
|
+
id: string;
|
|
6
|
+
}
|
|
7
|
+
interface Props {
|
|
8
|
+
total: number;
|
|
9
|
+
min: number | undefined;
|
|
10
|
+
items: TimelineItem[];
|
|
11
|
+
selectedIndex?: number;
|
|
12
|
+
now: number;
|
|
13
|
+
timelinelWidth: number;
|
|
14
|
+
showZoomButtons?: boolean;
|
|
15
|
+
onZoom?: () => void;
|
|
16
|
+
zoom?: 'in' | 'out';
|
|
17
|
+
onSelectIteration?: (id: string) => void;
|
|
18
|
+
idToIterationIndex?: (id: string) => number | undefined;
|
|
19
|
+
showIterations?: string[];
|
|
20
|
+
isJobFailure?: (id: string) => boolean;
|
|
21
|
+
}
|
|
22
|
+
declare const FlowTimelineBar: import("svelte").Component<Props, {}, "">;
|
|
23
|
+
type FlowTimelineBar = ReturnType<typeof FlowTimelineBar>;
|
|
24
|
+
export default FlowTimelineBar;
|