Files
windmill/frontend/src/lib/components/copilot/ScriptFix.svelte
Ruben Fiszel 310f37a682 feat: migrate to svelte5 + vite6 (#4813)
* runs on svelte 5

* Line component from svelte-chartjs

* Replaced all svelte-chartjs occurrences with custom wrapper

* Fix props mistake

* Fix illegal table structures

* self-closing-tags fix

* aria labels

* Fixed trivial warnings and errors

* @tanstack/svelte-table fix

* upgrade to vite 6

* svelte-kit sync before running svelte-check

* Remove on:clear which is actually on:removeAll and already handled by on:change

* fix worker tags not displaying in Autoscaling

* Try to fix svelte-kit sync not working during CI

* remove warnings

* Fix add flow page crashing

* access worldStore before assignment fix

* fix infinite recursions in App Editor

* Replaced JSON.stringify with proper deepEqual

* component mount api changed (no longer classes)

* fix ci errors

* Fix infinite loops in background runnable panel

* factored effect on deep equal logic in onObjChange

* fix "Add" not working in AgGrid Table

* Replaced legacy component.$set api

* Fix multiselect infinite value reaction

* Fix flow input fields resetting when opening their edit tab

* fix date input resetting when typing year

* Remove !p-0 affecting subgrid dotted borders

* fix missing debounceTemplate causing hundreds of updates

* Fix AgGrid action refreshes and disppearing

* resolve getItems generating random ids every rerun

* fix cannot access items before init

* fix sort lambda arguments being undefined

* Revert "Remove !p-0 affecting subgrid dotted borders"

This reverts commit c62809bb45d682a48376b071680645ed4e1c601b.

* fix input not updating in decision tree editor

* Update frontend/src/lib/components/schema/EditableSchemaWrapper.svelte

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

* Re-added padding affecting subgrid dotted borders (#5479)

* remove !p-0 in preset components

* removed extra padding on accordion tabs subgrid

* Fix non-reactive SchemaForm

* dirty fix for the oneOf bug

* Fix warnings and update svelte-exmarkdown for svelte 5

* fix dnd not working

* don't mount component like objects

---------

Co-authored-by: Diego Imbert <diegoimbert@protonmail.com>
Co-authored-by: Diego Imbert <70353967+diegoimbert@users.noreply.github.com>
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-04-02 21:33:50 +00:00

251 lines
6.0 KiB
Svelte

<script lang="ts">
import { base } from '$lib/base'
import { Button } from '../common'
import { SUPPORTED_LANGUAGES, copilot } from './lib'
import type { SupportedLanguage } from '$lib/common'
import { sendUserToast } from '$lib/toast'
import type Editor from '../Editor.svelte'
import { dbSchemas, copilotInfo, type DBSchema, workspaceStore } from '$lib/stores'
import type DiffEditor from '../DiffEditor.svelte'
import { scriptLangToEditorLang } from '$lib/scripts'
import Popover from '$lib/components/meltComponents/Popover.svelte'
import { writable } from 'svelte/store'
import { WindmillIcon } from '../icons'
import HighlightCode from '../HighlightCode.svelte'
import LoadingIcon from '../apps/svelte-select/lib/LoadingIcon.svelte'
import { autoPlacement } from '@floating-ui/core'
import { Check, Wand2, X, RotateCw } from 'lucide-svelte'
import { createEventDispatcher } from 'svelte'
// props
export let lang: SupportedLanguage
export let editor: Editor | undefined
export let diffEditor: DiffEditor | undefined
export let error: string
export let args: Record<string, any>
export let chatMode: boolean = false
// state
let genLoading: boolean = false
let generatedCode = writable<string>('')
let generatedExplanation = writable<string>('')
let dbSchema: DBSchema | undefined = undefined
let abortController: AbortController | undefined = undefined
const dispatch = createEventDispatcher<{
fix: null
}>()
async function onFix() {
if (!error) {
return
}
try {
genLoading = true
abortController = new AbortController()
await copilot(
{
language: lang,
code: editor?.getCode() || '',
error,
dbSchema: dbSchema,
type: 'fix',
workspace: $workspaceStore!
},
generatedCode,
abortController,
generatedExplanation
)
setupDiff()
diffEditor?.setModified($generatedCode)
showDiff()
} catch (err) {
if (!abortController?.signal.aborted) {
if (err?.message) {
sendUserToast('Failed to generate code: ' + err.message, true)
} else {
sendUserToast('Failed to generate code', true)
console.error(err)
}
}
} finally {
genLoading = false
}
}
function acceptDiff() {
editor?.setCode(diffEditor?.getModified() || '')
editor?.format()
clear()
}
function rejectDiff() {
clear()
}
function setupDiff() {
diffEditor?.setupModel(scriptLangToEditorLang(lang))
diffEditor?.setOriginal(editor?.getCode() || '')
}
function showDiff() {
diffEditor?.show()
editor?.hide()
}
function hideDiff() {
editor?.show()
diffEditor?.hide()
}
function clear() {
$generatedCode = ''
$generatedExplanation = ''
}
$: lang && clear()
$: !$generatedCode && hideDiff()
function updateSchema(lang, args) {
const schemaRes = lang === 'graphql' ? args.api : args.database
if (typeof schemaRes === 'string') {
dbSchema = $dbSchemas[schemaRes.replace('$res:', '')]
}
}
$: updateSchema(lang, args)
let popover: Popover | undefined = undefined
</script>
{#if SUPPORTED_LANGUAGES.has(lang)}
{#if !genLoading && $generatedCode.length > 0}
<div class="flex gap-1">
<Button
title="Discard generated code"
size="xs"
color="red"
spacingSize="xs2"
on:click={() => {
popover?.close()
rejectDiff()
}}
variant="contained"
startIcon={{ icon: X }}
propagateEvent={true}
>
Discard
</Button>
<Button
title="Accept generated code"
size="xs"
color="green"
spacingSize="xs2"
on:click={() => {
popover?.close()
acceptDiff()
}}
startIcon={{ icon: Check }}
propagateEvent={true}
>
Accept
</Button>
<Button
size="xs"
color="light"
spacingSize="xs2"
on:click={() => {
$generatedExplanation = ''
popover?.open()
onFix()
}}
startIcon={{ icon: RotateCw }}
title="Retry"
btnClasses="text-violet-800 dark:text-violet-400"
/>
</div>
{/if}
<Popover
bind:this={popover}
floatingConfig={{
middleware: [
autoPlacement({
allowedPlacements: ['bottom-end', 'top-end']
})
]
}}
closeOnOutsideClick={!genLoading}
closeButton={!genLoading}
displayArrow={true}
>
<svelte:fragment slot="trigger" let:isOpen>
<div class="flex flex-row">
<Button
title="Fix code"
size="xs"
color={genLoading ? 'red' : 'light'}
spacingSize="xs2"
startIcon={genLoading ? undefined : { icon: Wand2 }}
propagateEvent={!chatMode}
on:click={chatMode
? () => dispatch('fix')
: genLoading
? () => abortController?.abort()
: $generatedCode.length > 0
? () => {}
: () => onFix()}
btnClasses={genLoading
? ''
: 'text-violet-800 dark:text-violet-400 bg-violet-100 dark:bg-gray-700 min-w-[84px]'}
>
{#if genLoading}
<WindmillIcon
white
class="mr-1 text-white"
height="16px"
width="20px"
spin="veryfast"
/>
Stop
{:else if $generatedCode.length > 0}
<span class="text-xs">{isOpen ? 'Hide' : 'Show'}</span>
{:else}
AI Fix
{/if}
</Button>
</div>
</svelte:fragment>
<svelte:fragment slot="content">
<div class="p-4">
{#if $copilotInfo.enabled}
<div class="w-[42rem] min-h-[3rem] max-h-[34rem] overflow-y-auto">
{#if $generatedCode.length > 0 && genLoading}
<div class="overflow-x-scroll">
<HighlightCode language={lang} code={$generatedCode} />
</div>
{:else if genLoading}
<LoadingIcon />
{/if}
{#if $generatedExplanation.length > 0}
<p class="text-sm mt-2"
><span class="font-bold">Explanation: test</span> {$generatedExplanation}</p
>
{/if}
</div>
{:else}
<div class="w-80">
<p class="text-sm"
>Enable Windmill AI in the <a
class="inline-flex flex-row items-center gap-1"
href="{base}/workspace_settings?tab=ai"
target="_blank">workspace settings</a
></p
></div
>
{/if}
</div>
</svelte:fragment>
</Popover>
{/if}