mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-08 16:03:27 +00:00
249 lines
6.4 KiB
Svelte
249 lines
6.4 KiB
Svelte
<script lang="ts">
|
|
import { Button } from '../common'
|
|
|
|
import { SUPPORTED_LANGUAGES, editScript, generateScript } from './lib'
|
|
import type { SupportedLanguage } from '$lib/common'
|
|
import { sendUserToast } from '$lib/toast'
|
|
import type Editor from '../Editor.svelte'
|
|
import { faCheck, faClose, faMagicWandSparkles } from '@fortawesome/free-solid-svg-icons'
|
|
import Popup from '../common/popup/Popup.svelte'
|
|
import { Icon } from 'svelte-awesome'
|
|
import { dbSchema, dbSchemaPublicOnly, existsOpenaiResourcePath } from '$lib/stores'
|
|
import type DiffEditor from '../DiffEditor.svelte'
|
|
import { scriptLangToEditorLang } from '$lib/scripts'
|
|
import type { Selection } from 'monaco-editor/esm/vs/editor/editor.api'
|
|
import type SimpleEditor from '../SimpleEditor.svelte'
|
|
import Tooltip from '../Tooltip.svelte'
|
|
import ToggleButtonGroup from '../common/toggleButton-v2/ToggleButtonGroup.svelte'
|
|
import ToggleButton from '../common/toggleButton-v2/ToggleButton.svelte'
|
|
|
|
// props
|
|
export let iconOnly: boolean = false
|
|
export let lang: SupportedLanguage | 'frontend'
|
|
export let editor: Editor | SimpleEditor | undefined
|
|
export let diffEditor: DiffEditor | undefined
|
|
export let inlineScript = false
|
|
|
|
// state
|
|
let funcDesc: string = ''
|
|
let genLoading: boolean = false
|
|
let button: HTMLButtonElement | undefined
|
|
let input: HTMLInputElement | undefined
|
|
let generatedCode = ''
|
|
let selection: Selection | undefined
|
|
let isEdit = false
|
|
|
|
async function onGenerate() {
|
|
if (funcDesc.length <= 0) {
|
|
return
|
|
}
|
|
try {
|
|
genLoading = true
|
|
if (isEdit && selection) {
|
|
const selectedCode = editor?.getSelectedLines() || ''
|
|
const originalCode = editor?.getCode() || ''
|
|
const result = await editScript({
|
|
language: lang,
|
|
description: funcDesc,
|
|
selectedCode,
|
|
dbSchema: $dbSchema,
|
|
dbSchemaPublicOnly: $dbSchemaPublicOnly
|
|
})
|
|
generatedCode = originalCode.replace(selectedCode, result.code + '\n')
|
|
} else {
|
|
const result = await generateScript({
|
|
language: lang,
|
|
description: funcDesc,
|
|
dbSchema: $dbSchema,
|
|
dbSchemaPublicOnly: $dbSchemaPublicOnly
|
|
})
|
|
generatedCode = result.code
|
|
}
|
|
funcDesc = ''
|
|
} catch (err) {
|
|
sendUserToast('Failed to generate code', true)
|
|
console.error(err)
|
|
} finally {
|
|
genLoading = false
|
|
}
|
|
}
|
|
|
|
function acceptDiff() {
|
|
editor?.setCode(diffEditor?.getModified() || '')
|
|
editor?.format()
|
|
generatedCode = ''
|
|
}
|
|
|
|
function rejectDiff() {
|
|
generatedCode = ''
|
|
}
|
|
|
|
function showDiff() {
|
|
diffEditor?.setDiff(
|
|
editor?.getCode() || '',
|
|
generatedCode,
|
|
lang === 'frontend' ? 'javascript' : scriptLangToEditorLang(lang)
|
|
)
|
|
diffEditor?.show()
|
|
editor?.hide()
|
|
}
|
|
|
|
function hideDiff() {
|
|
editor?.show()
|
|
diffEditor?.hide()
|
|
}
|
|
|
|
function setSelectionHandler() {
|
|
editor?.onDidChangeCursorSelection((e) => {
|
|
selection = e.selection
|
|
})
|
|
}
|
|
|
|
$: input?.focus()
|
|
|
|
$: lang && (generatedCode = '')
|
|
|
|
$: generatedCode && showDiff()
|
|
$: !generatedCode && hideDiff()
|
|
$: editor && setSelectionHandler()
|
|
$: selection && (isEdit = !selection.isEmpty())
|
|
</script>
|
|
|
|
{#if generatedCode}
|
|
{#if inlineScript}
|
|
<div class="flex gap-1">
|
|
<Button
|
|
title="Discard generated code"
|
|
btnClasses="!font-medium px-2 w-7"
|
|
size="xs"
|
|
color="red"
|
|
on:click={rejectDiff}
|
|
variant="contained"
|
|
>
|
|
<Icon data={faClose} />
|
|
</Button><Button
|
|
title="Accept generated code"
|
|
btnClasses="!font-medium px-2 w-7"
|
|
size="xs"
|
|
color="green"
|
|
on:click={acceptDiff}
|
|
>
|
|
<Icon data={faCheck} /></Button
|
|
>
|
|
</div>
|
|
{:else}
|
|
<div class="flex gap-1 px-2">
|
|
<Button
|
|
title="Discard generated code"
|
|
btnClasses="!font-medium px-2"
|
|
size="xs"
|
|
color="red"
|
|
on:click={rejectDiff}
|
|
variant="contained"
|
|
startIcon={{ icon: faClose }}
|
|
{iconOnly}
|
|
>
|
|
Discard
|
|
</Button><Button
|
|
title="Accept generated code"
|
|
btnClasses="!font-medium px-2"
|
|
size="xs"
|
|
color="green"
|
|
on:click={acceptDiff}
|
|
startIcon={{ icon: faCheck }}
|
|
{iconOnly}
|
|
>
|
|
Accept
|
|
</Button>
|
|
</div>
|
|
{/if}
|
|
{/if}
|
|
{#if !generatedCode && SUPPORTED_LANGUAGES.has(lang)}
|
|
<Popup floatingConfig={{ placement: 'bottom-end', strategy: 'absolute' }} let:close>
|
|
<svelte:fragment slot="button">
|
|
{#if inlineScript}
|
|
<Button
|
|
size="lg"
|
|
bind:element={button}
|
|
color="light"
|
|
btnClasses="!px-2 !bg-surface-secondary hover:!bg-surface-hover"
|
|
loading={genLoading}
|
|
nonCaptureEvent={true}
|
|
>
|
|
<Icon scale={0.8} data={faMagicWandSparkles} />
|
|
</Button>
|
|
{:else}
|
|
<Button
|
|
title="Generate code from prompt"
|
|
btnClasses="!font-medium text-secondary"
|
|
size="xs"
|
|
color="light"
|
|
spacingSize="md"
|
|
bind:element={button}
|
|
startIcon={{ icon: faMagicWandSparkles }}
|
|
{iconOnly}
|
|
loading={genLoading}
|
|
nonCaptureEvent={true}
|
|
>
|
|
{isEdit ? 'AI Edit' : 'AI Gen'}
|
|
</Button>
|
|
{/if}
|
|
</svelte:fragment>
|
|
<label class="block text-primary">
|
|
{#if $existsOpenaiResourcePath}
|
|
<div class="flex w-96">
|
|
<input
|
|
type="text"
|
|
bind:this={input}
|
|
bind:value={funcDesc}
|
|
on:keypress={({ key }) => {
|
|
if (key === 'Enter' && funcDesc.length > 0) {
|
|
close(input || null)
|
|
onGenerate()
|
|
}
|
|
}}
|
|
placeholder={isEdit
|
|
? 'Describe the changes you want'
|
|
: 'Describe what the script should do'}
|
|
/>
|
|
<Button
|
|
size="xs"
|
|
color="blue"
|
|
buttonType="button"
|
|
btnClasses="!p-1 !w-[38px] !ml-2"
|
|
aria-label="Generate"
|
|
on:click={() => {
|
|
close(input || null)
|
|
onGenerate()
|
|
}}
|
|
disabled={funcDesc.length <= 0}
|
|
>
|
|
<Icon data={faMagicWandSparkles} />
|
|
</Button>
|
|
</div>
|
|
{#if ['postgresql', 'mysql'].includes(lang) && $dbSchema}
|
|
<div class="flex flex-row items-center justify-between w-96 mt-2">
|
|
<p class="text-sm">
|
|
Will take into account the DB schema
|
|
<Tooltip>
|
|
In order to better generate the script, we pass the selected DB schema to GPT-4.
|
|
</Tooltip>
|
|
</p>
|
|
{#if lang === 'postgresql'}
|
|
<ToggleButtonGroup class="w-auto shrink-0" bind:selected={$dbSchemaPublicOnly}>
|
|
<ToggleButton value={true} label="Public schema" />
|
|
<ToggleButton value={false} label="All schemas" />
|
|
</ToggleButtonGroup>
|
|
{/if}
|
|
</div>
|
|
{/if}
|
|
{:else}
|
|
<p class="text-sm"
|
|
>Enable Windmill AI in the <a href="/workspace_settings?tab=openai">workspace settings.</a
|
|
></p
|
|
>
|
|
{/if}
|
|
</label>
|
|
</Popup>
|
|
{/if}
|