Files
windmill/frontend/src/lib/components/flows/map/FlowErrorHandlerItem.svelte
T
Guilhem LemouelandClaude Opus 5 a582eb526e chore(frontend): remove the tutorial system
Deletes the guided-tour feature: the tutorials directory, the per-editor
wrappers, the home banner and button, the /tutorials route, and the
driver.js dependency they were built on. Also removes what only existed to
serve them — the `tutorialsToDo` / `skippedAll` / `isCurrentlyInTutorial`
stores, the `disableTutorials` prop chain through the flow editor, the
`?tutorial=` deep links, PopupV2's clickOutside exemption for the driver
popover, and the selector-anchor class on the flow editor tabs.

The backend `tutorial_progress` endpoints and table stay: nothing calls
them now, and removing them is a public-API break plus a migration that
would drop existing progress.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012fRjnaHLwjpHN84gNNxah9
2026-09-02 10:15:03 +02:00

160 lines
4.7 KiB
Svelte

<script lang="ts">
import type { FlowEditorContext } from '../types'
import type { FlowDiffManager } from '../flowDiffManager.svelte'
import { createEventDispatcher, getContext } from 'svelte'
import { Bug, X } from 'lucide-svelte'
import InsertModulePopover from '$lib/components/flows/map/InsertModulePopover.svelte'
import { insertNewFailureModule } from '$lib/components/flows/flowStateUtils.svelte'
import type { RawScript, ScriptLang } from '$lib/gen'
import { twMerge } from 'tailwind-merge'
import { refreshFlowStateStore } from '$lib/components/flows/flowStoreRefresh.svelte'
import Button from '$lib/components/common/button/Button.svelte'
import DiffActionBar from './DiffActionBar.svelte'
import { getNodeColorClasses, aiActionToNodeState } from '$lib/components/graph'
let {
disableAi,
small,
diffManager,
compact = false
}: {
small: boolean
disableAi?: boolean
diffManager?: FlowDiffManager
compact?: boolean
} = $props()
const dispatch = createEventDispatcher<{
generateStep: { moduleId: string; instructions: string; lang: ScriptLang }
}>()
const { selectionManager, flowStateStore, flowStore, opWorkspace } =
getContext<FlowEditorContext>('FlowEditorContext')
const failureModuleId = $derived(flowStore.val?.value?.failure_module?.id)
const moduleAction = $derived(
failureModuleId ? diffManager?.moduleActions?.[failureModuleId] : undefined
)
const aiColorClasses = $derived(
moduleAction ? getNodeColorClasses(aiActionToNodeState(moduleAction.action), false) : undefined
)
async function insertFailureModule(
inlineScript?: {
language: RawScript['language']
subkind: 'pgsql' | 'flow'
instructions?: string
},
wsScript?: { path: string; summary: string; hash: string | undefined }
) {
await insertNewFailureModule(flowStore, flowStateStore, inlineScript, wsScript, opWorkspace?.())
if (inlineScript?.instructions) {
dispatch('generateStep', {
moduleId: 'failure',
instructions: inlineScript.instructions,
lang: inlineScript.language
})
}
selectionManager.selectId('failure')
refreshFlowStateStore(flowStore)
}
function deleteFailureModule() {
flowStore.val.value.failure_module = undefined
// The panel has to land somewhere once the error handler is gone, but that is a
// consequence of the delete — not a request to see the flow's settings.
selectionManager.selectId('settings-metadata', { openPanel: false })
}
const smallFailureModule = $derived(!(failureModuleId && diffManager && moduleAction) && compact)
</script>
{#if flowStore.val?.value?.failure_module}
<!-- svelte-ignore a11y_click_events_have_key_events -->
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div class="relative">
<Button
variant="default"
unifiedSize="sm"
wrapperClasses={compact ? undefined : twMerge('min-w-36', small ? 'max-w-52' : 'max-w-64')}
id="flow-editor-error-handler"
selected={selectionManager.getSelectedId()?.includes('failure')}
onClick={() => {
if (flowStore.val?.value?.failure_module) {
selectionManager.selectId('failure')
}
}}
btnClasses={aiColorClasses?.bg ?? ''}
>
{#if failureModuleId}
<DiffActionBar
moduleId={failureModuleId}
{moduleAction}
{diffManager}
{flowStore}
placement="bottom"
/>
{/if}
<Bug size={14} class="shrink-0" />
{#if !smallFailureModule}
<div class="truncate grow min-w-0 text-center text-xs">
{flowStore.val.value.failure_module?.summary ||
(flowStore.val.value.failure_module?.value.type === 'rawscript'
? `${flowStore.val.value.failure_module?.value.language}`
: 'TBD')}
</div>
<button
title="Delete failure script"
type="button"
class="ml-1"
onclick={deleteFailureModule}
>
<X size={12} />
</button>
{/if}
</Button>
{#if smallFailureModule}
<button
title="Delete failure script"
type="button"
class="absolute -top-1.5 -right-1.5 rounded-full bg-surface border border-border p-0.5 hover:bg-surface-hover"
onclick={deleteFailureModule}
>
<X size={10} />
</button>
{/if}
</div>
{:else}
<InsertModulePopover
{disableAi}
placement={'bottom'}
on:new={(e) => {
insertFailureModule(e.detail.inlineScript)
}}
on:pickScript={(e) => {
insertFailureModule(undefined, e.detail)
}}
kind="failure"
>
{#snippet trigger()}
<Button
unifiedSize="sm"
wrapperClasses={compact ? undefined : 'min-w-36'}
title={`Add failure module`}
variant="default"
id={`flow-editor-add-step-error-handler-button`}
nonCaptureEvent
startIcon={{ icon: Bug }}
iconOnly={compact}
>
{#if !compact}
Error Handler
{/if}
</Button>
{/snippet}
</InsertModulePopover>
{/if}