mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-22 08:02:19 +00:00
feat: new ai design (#3152)
* feat: new ai design * fix: show caret + esc to cancel
This commit is contained in:
@@ -522,7 +522,7 @@
|
||||
</Button>
|
||||
{/if}
|
||||
{#if collabMode}
|
||||
<div class="flex items-center px-1">
|
||||
<div class="flex items-center px-3">
|
||||
<Toggle
|
||||
options={{ right: '' }}
|
||||
size="xs"
|
||||
@@ -531,13 +531,13 @@
|
||||
/>
|
||||
<Popover>
|
||||
<svelte:fragment slot="text">Multiplayer</svelte:fragment>
|
||||
<Users class="ml-1" size={12} />
|
||||
<Users class="ml-1 text-tertiary" size={14} />
|
||||
</Popover>
|
||||
{#if collabLive}
|
||||
<button
|
||||
title="Show invite link"
|
||||
class="p-1 rounded hover:bg-gray-400 mx-1 border"
|
||||
on:click={() => dispatch('collabPopup')}><Link size={12} /></button
|
||||
on:click={() => dispatch('collabPopup')}><Link size={14} /></button
|
||||
>
|
||||
<div class="isolate flex -space-x-2 pl-2">
|
||||
{#each collabUsers as user}
|
||||
@@ -555,35 +555,13 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<ScriptGen {editor} {diffEditor} {lang} {iconOnly} {args} />
|
||||
|
||||
<CodeCompletionStatus />
|
||||
|
||||
<FormatOnSave />
|
||||
<!-- <Popover
|
||||
notClickable
|
||||
placement="bottom"
|
||||
disappearTimeout={0}
|
||||
class="px-1"
|
||||
disablePopup={!iconOnly}
|
||||
>
|
||||
<Button
|
||||
btnClasses="!font-medium"
|
||||
size="xs"
|
||||
spacingSize="md"
|
||||
color="light"
|
||||
on:click={editor?.format}
|
||||
{iconOnly}
|
||||
startIcon={{ icon: faBroom }}
|
||||
>
|
||||
Format ({getModifierKey()}+S)
|
||||
</Button>
|
||||
<svelte:fragment slot="text">
|
||||
Format <Kbd class="!text-gray-800">{getModifierKey()}</Kbd> + <Kbd class="!text-gray-800">
|
||||
S
|
||||
</Kbd>
|
||||
</svelte:fragment>
|
||||
</Popover> -->
|
||||
|
||||
<div class="flex flex-row gap-1">
|
||||
<ScriptGen {editor} {diffEditor} {lang} {iconOnly} {args} />
|
||||
|
||||
<CodeCompletionStatus />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import { formatOnSave } from '$lib/stores'
|
||||
import Popover from './Popover.svelte'
|
||||
import { getLocalSetting, storeLocalSetting } from '$lib/utils'
|
||||
import PaintbrushOff from './icons/PaintbrushOff.svelte'
|
||||
|
||||
const SETTING_NAME = 'formatOnSave'
|
||||
function loadSetting() {
|
||||
@@ -23,8 +24,12 @@
|
||||
>{$formatOnSave ? 'Disable' : 'Enable'} auto-formatting</svelte:fragment
|
||||
>
|
||||
<Button
|
||||
size="xs"
|
||||
color="light"
|
||||
startIcon={{ icon: Paintbrush, classes: !$formatOnSave ? 'text-red-400' : '' }}
|
||||
startIcon={{
|
||||
icon: !$formatOnSave ? PaintbrushOff : Paintbrush
|
||||
}}
|
||||
btnClasses="text-tertiary"
|
||||
on:click={() => {
|
||||
storeSetting()
|
||||
}}
|
||||
|
||||
@@ -418,6 +418,9 @@
|
||||
code={script.content}
|
||||
configName="summary"
|
||||
let:updateFocus
|
||||
let:active
|
||||
let:classNames
|
||||
focused
|
||||
on:change={() => {
|
||||
if (initialPath == '' && script.summary?.length > 0 && !dirtyPath) {
|
||||
path?.setName(
|
||||
@@ -433,10 +436,11 @@
|
||||
<input
|
||||
type="text"
|
||||
autofocus
|
||||
class={classNames}
|
||||
on:focus={() => updateFocus(true)}
|
||||
on:blur={() => updateFocus(false)}
|
||||
bind:value={script.summary}
|
||||
placeholder={'Short summary to be displayed when listed'}
|
||||
placeholder={!active ? 'Short summary to be displayed when listed' : ''}
|
||||
/>
|
||||
</MetadataGen>
|
||||
<Label label="Path">
|
||||
@@ -459,15 +463,17 @@
|
||||
el={descriptionTextArea}
|
||||
label="Description"
|
||||
let:updateFocus
|
||||
let:active
|
||||
let:classNames
|
||||
>
|
||||
<textarea
|
||||
on:focus={() => updateFocus(true)}
|
||||
on:blur={() => updateFocus(false)}
|
||||
class={classNames}
|
||||
use:autosize
|
||||
bind:this={descriptionTextArea}
|
||||
bind:value={script.description}
|
||||
placeholder={'Description displayed in the details page'}
|
||||
class="text-sm"
|
||||
placeholder={!active ? 'Description displayed in the details page' : ''}
|
||||
/>
|
||||
</MetadataGen>
|
||||
</div>
|
||||
|
||||
@@ -22,68 +22,66 @@
|
||||
const bothOptions = Boolean(options.left) && Boolean(options.right)
|
||||
</script>
|
||||
|
||||
<span class="{$$props.class} z-auto">
|
||||
<label
|
||||
for={id}
|
||||
class="inline-flex items-center mt-2 duration-50 {disabled
|
||||
? 'grayscale opacity-50'
|
||||
: 'cursor-pointer'}"
|
||||
>
|
||||
{#if Boolean(options?.left)}
|
||||
<span
|
||||
class={twMerge(
|
||||
'mr-2 font-medium duration-50',
|
||||
bothOptions ? (checked ? 'text-disabled' : 'text-primary') : 'text-primary',
|
||||
size === 'xs' ? 'text-xs' : 'text-sm',
|
||||
textClass
|
||||
)}
|
||||
style={textStyle}
|
||||
>
|
||||
{options?.left}
|
||||
</span>
|
||||
{/if}
|
||||
<label
|
||||
for={id}
|
||||
class="{$$props.class || ''} z-auto inline-flex items-center duration-50 {disabled
|
||||
? 'grayscale opacity-50'
|
||||
: 'cursor-pointer'}"
|
||||
>
|
||||
{#if Boolean(options?.left)}
|
||||
<span
|
||||
class={twMerge(
|
||||
'mr-2 font-medium duration-50',
|
||||
bothOptions ? (checked ? 'text-disabled' : 'text-primary') : 'text-primary',
|
||||
size === 'xs' ? 'text-xs' : 'text-sm',
|
||||
textClass
|
||||
)}
|
||||
style={textStyle}
|
||||
>
|
||||
{options?.left}
|
||||
</span>
|
||||
{/if}
|
||||
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div class="relative" on:click|stopPropagation>
|
||||
<input
|
||||
on:focus
|
||||
on:click
|
||||
{disabled}
|
||||
type="checkbox"
|
||||
{id}
|
||||
class="sr-only peer"
|
||||
bind:checked
|
||||
on:change|stopPropagation={(e) => {
|
||||
dispatch('change', checked)
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
class={classNames(
|
||||
"transition-all bg-surface-selected rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:bg-surface after:border-white after:border after:rounded-full after:transition-all ",
|
||||
color == 'red' ? 'peer-checked:bg-red-600' : 'peer-checked:bg-blue-600',
|
||||
size === 'sm'
|
||||
? 'w-11 h-6 after:top-0.5 after:left-[2px] after:h-5 after:w-5'
|
||||
: 'w-7 h-4 after:top-0.5 after:left-[2px] after:h-3 after:w-3'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
{#if Boolean(options?.right)}
|
||||
<span
|
||||
class={twMerge(
|
||||
'ml-2 font-medium duration-50',
|
||||
bothOptions ? (checked ? 'text-primary' : 'text-disabled') : 'text-primary',
|
||||
size === 'xs' ? 'text-xs' : 'text-sm',
|
||||
textClass
|
||||
)}
|
||||
style={textStyle}
|
||||
>
|
||||
{options?.right}
|
||||
{#if options?.rightTooltip}
|
||||
<Tooltip>{options?.rightTooltip}</Tooltip>
|
||||
{/if}
|
||||
</span>
|
||||
{/if}
|
||||
<slot name="right" />
|
||||
</label>
|
||||
</span>
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div class="relative" on:click|stopPropagation>
|
||||
<input
|
||||
on:focus
|
||||
on:click
|
||||
{disabled}
|
||||
type="checkbox"
|
||||
{id}
|
||||
class="sr-only peer"
|
||||
bind:checked
|
||||
on:change|stopPropagation={(e) => {
|
||||
dispatch('change', checked)
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
class={classNames(
|
||||
"transition-all bg-surface-selected rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:bg-surface after:border-white after:border after:rounded-full after:transition-all ",
|
||||
color == 'red' ? 'peer-checked:bg-red-600' : 'peer-checked:bg-blue-600',
|
||||
size === 'sm'
|
||||
? 'w-11 h-6 after:top-0.5 after:left-[2px] after:h-5 after:w-5'
|
||||
: 'w-7 h-4 after:top-0.5 after:left-[2px] after:h-3 after:w-3'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
{#if Boolean(options?.right)}
|
||||
<span
|
||||
class={twMerge(
|
||||
'ml-2 font-medium duration-50',
|
||||
bothOptions ? (checked ? 'text-primary' : 'text-disabled') : 'text-primary',
|
||||
size === 'xs' ? 'text-xs' : 'text-sm',
|
||||
textClass
|
||||
)}
|
||||
style={textStyle}
|
||||
>
|
||||
{options?.right}
|
||||
{#if options?.rightTooltip}
|
||||
<Tooltip>{options?.rightTooltip}</Tooltip>
|
||||
{/if}
|
||||
</span>
|
||||
{/if}
|
||||
<slot name="right" />
|
||||
</label>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { ZapIcon, ZapOffIcon } from 'lucide-svelte'
|
||||
import { SparklesIcon } from 'lucide-svelte'
|
||||
import Button from '../common/button/Button.svelte'
|
||||
import { codeCompletionLoading, copilotInfo, codeCompletionSessionEnabled } from '$lib/stores'
|
||||
import Popover from '../Popover.svelte'
|
||||
import { getLocalSetting, storeLocalSetting } from '$lib/utils'
|
||||
import SparklesOffIcon from '../icons/SparklesOffIcon.svelte'
|
||||
|
||||
const SETTING_NAME = 'codeCompletionSessionEnabled'
|
||||
function loadSetting() {
|
||||
@@ -21,19 +22,26 @@
|
||||
{#if $copilotInfo.exists_openai_resource_path && $copilotInfo.code_completion_enabled}
|
||||
<Popover>
|
||||
<svelte:fragment slot="text"
|
||||
>{$codeCompletionSessionEnabled ? 'Disable' : 'Enable'} code completion (applies only to you)</svelte:fragment
|
||||
>Code completion: {$codeCompletionSessionEnabled ? 'disabled' : 'enabled'} (applies only to you)</svelte:fragment
|
||||
>
|
||||
<Button
|
||||
color="light"
|
||||
size="xs"
|
||||
loading={$codeCompletionLoading}
|
||||
startIcon={$codeCompletionLoading
|
||||
? undefined
|
||||
: {
|
||||
icon: $codeCompletionSessionEnabled ? ZapIcon : ZapOffIcon
|
||||
icon: $codeCompletionSessionEnabled ? SparklesIcon : SparklesOffIcon,
|
||||
classes: $codeCompletionSessionEnabled
|
||||
? 'text-sky-900 dark:text-sky-200'
|
||||
: 'text-sky-900/50 dark:text-sky-200/50'
|
||||
}}
|
||||
on:click={() => {
|
||||
storeSetting()
|
||||
}}
|
||||
btnClasses={$codeCompletionSessionEnabled
|
||||
? 'bg-sky-50 dark:bg-gray-700 dark:hover:bg-surface-hover'
|
||||
: ''}
|
||||
/>
|
||||
</Popover>
|
||||
{:else}
|
||||
@@ -44,7 +52,7 @@
|
||||
<Button
|
||||
color="light"
|
||||
startIcon={{
|
||||
icon: ZapOffIcon
|
||||
icon: SparklesOffIcon
|
||||
}}
|
||||
disabled
|
||||
/>
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
startIcon={{ icon: Wand2 }}
|
||||
iconOnly
|
||||
title="AI Assistant"
|
||||
btnClasses="min-h-[30px]"
|
||||
btnClasses="min-h-[30px] text-sky-900 dark:text-sky-200 bg-sky-100 dark:bg-gray-700 hover:bg-surface-hover dark:hover:bg-surface-hover"
|
||||
loading={genLoading}
|
||||
clickableWhileLoading
|
||||
on:click={genLoading ? () => abortController?.abort() : undefined}
|
||||
@@ -87,7 +87,7 @@
|
||||
color="dark"
|
||||
variant="contained"
|
||||
buttonType="button"
|
||||
btnClasses="!p-1 !w-[38px] !ml-2"
|
||||
btnClasses="!p-1 !w-[38px] !ml-2 text-sky-900 dark:text-sky-200 bg-sky-100 dark:bg-gray-700 hover:bg-surface-hover dark:hover:bg-surface-hover"
|
||||
title="Generate CRON schedule from prompt"
|
||||
aria-label="Generate"
|
||||
iconOnly
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
import type { FlowModule } from '$lib/gen'
|
||||
import type { FlowEditorContext } from '../flows/types'
|
||||
import { ExternalLink, Wand2 } from 'lucide-svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
export let copilotLoading: boolean
|
||||
export let copilotStatus: string
|
||||
@@ -33,7 +34,12 @@
|
||||
<ManualPopover bind:this={copilotPopover}>
|
||||
<Button
|
||||
size="xs"
|
||||
btnClasses={$currentStepStore !== undefined ? 'z-[901]' : ''}
|
||||
btnClasses={twMerge(
|
||||
$currentStepStore !== undefined ? 'z-[901]' : '',
|
||||
copilotLoading || ($currentStepStore !== undefined && $currentStepStore !== 'Input')
|
||||
? ''
|
||||
: 'bg-sky-100 dark:bg-gray-700 text-sky-900 dark:text-sky-200 dark:hover:bg-surface-hover'
|
||||
)}
|
||||
on:click={() => {
|
||||
if (copilotLoading || ($currentStepStore !== undefined && $currentStepStore !== 'Input')) {
|
||||
abortController?.abort()
|
||||
@@ -119,6 +125,7 @@
|
||||
startIcon={{
|
||||
icon: Wand2
|
||||
}}
|
||||
btnClasses="bg-sky-100 dark:bg-gray-700 text-sky-900 dark:text-sky-200 dark:hover:bg-surface-hover"
|
||||
color={'light'}
|
||||
variant={'border'}
|
||||
nonCaptureEvent
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import type { ChatCompletionMessageParam } from 'openai/resources'
|
||||
import { copilotInfo, metadataCompletionEnabled } from '$lib/stores'
|
||||
import Label from '../Label.svelte'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { createEventDispatcher, onDestroy } from 'svelte'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
|
||||
type Config = {
|
||||
@@ -58,10 +58,14 @@ The description should focus on what it does and should not contain what concept
|
||||
export let el: HTMLElement | undefined = undefined
|
||||
export let label: string
|
||||
|
||||
let generatedContent = ''
|
||||
let genEl: HTMLElement | undefined = undefined
|
||||
let active = false
|
||||
let loading = false
|
||||
let abortController = new AbortController()
|
||||
let manualDisabled = false
|
||||
|
||||
let focused = false
|
||||
export let focused = false
|
||||
const updateFocus = (val) => {
|
||||
focused = val
|
||||
}
|
||||
@@ -83,13 +87,12 @@ The description should focus on what it does and should not contain what concept
|
||||
}
|
||||
]
|
||||
const response = await getCompletion(messages, abortController)
|
||||
content = ''
|
||||
generatedContent = ''
|
||||
for await (const chunk of response) {
|
||||
const toks = chunk.choices[0]?.delta?.content || ''
|
||||
content += toks
|
||||
if (el !== undefined) {
|
||||
el.style.height = 'auto'
|
||||
el.style.height = el.scrollHeight + 'px'
|
||||
generatedContent += toks
|
||||
if (el !== undefined && genEl !== undefined) {
|
||||
el.style.height = Math.max(genEl.scrollHeight + 34, 58) + 'px'
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -121,46 +124,87 @@ The description should focus on what it does and should not contain what concept
|
||||
$: if (content) {
|
||||
dispatch('change', { content })
|
||||
}
|
||||
|
||||
$: active =
|
||||
$copilotInfo.exists_openai_resource_path &&
|
||||
$metadataCompletionEnabled &&
|
||||
!content &&
|
||||
(loading || focused || !!generatedContent) &&
|
||||
!manualDisabled
|
||||
|
||||
$: focused && (manualDisabled = false)
|
||||
|
||||
onDestroy(() => {
|
||||
abortController.abort()
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="relative">
|
||||
<div class="flex flex-row" />
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<Label {label}>
|
||||
<div slot="header" class="flex flex-row pl-1 gap-2 items-center">
|
||||
{#if $copilotInfo.exists_openai_resource_path && $metadataCompletionEnabled}
|
||||
{#if loading}
|
||||
<Loader2 class="animate-spin text-gray-400" size={18} />
|
||||
<span class="text-xs">
|
||||
<span class="border px-1 py-0.5 rounded-md text-2xs text-bold bg-white text-black">
|
||||
ESC
|
||||
</span> to cancel
|
||||
</span>
|
||||
{:else if !content && focused}
|
||||
<span class="text-xs">
|
||||
<span class="border px-1 py-0.5 rounded-md text-2xs text-bold bg-white text-black">
|
||||
TAB
|
||||
</span> to generate
|
||||
</span>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
<div
|
||||
class="relative"
|
||||
on:keydown={(event) => {
|
||||
if (!$copilotInfo.exists_openai_resource_path || !$metadataCompletionEnabled) {
|
||||
return
|
||||
}
|
||||
if (event.key === 'Tab' && !loading && !content) {
|
||||
event.preventDefault()
|
||||
generateContent()
|
||||
} else if (event.key === 'Escape' && loading) {
|
||||
if (event.key === 'Tab') {
|
||||
if (!loading && generatedContent) {
|
||||
event.preventDefault()
|
||||
content = generatedContent
|
||||
generatedContent = ''
|
||||
} else if (!loading && !content) {
|
||||
event.preventDefault()
|
||||
generateContent()
|
||||
}
|
||||
} else if (event.key === 'Escape') {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
abortController.abort()
|
||||
if (loading) {
|
||||
abortController.abort()
|
||||
} else {
|
||||
manualDisabled = true
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
<slot {updateFocus} />
|
||||
<div
|
||||
class={'absolute left-0.5 flex flex-row pl-1 gap-2 items-start top-[0.3rem] pointer-events-none'}
|
||||
>
|
||||
{#if active}
|
||||
<span
|
||||
class="absolute text-xs text-sky-900 bg-sky-100 dark:text-sky-200 dark:bg-gray-700 p-1 rounded-md flex flex-row items-center justify-center gap-2 w-32 shrink-0"
|
||||
>
|
||||
{#if loading}
|
||||
<Loader2 class="animate-spin text-gray-500 dark:text-gray-400" size={16} />
|
||||
{/if}
|
||||
<div>
|
||||
<span class="px-1 py-0.5 rounded-md text-2xs text-bold bg-white dark:bg-surface">
|
||||
{#if loading}
|
||||
ESC
|
||||
{:else}
|
||||
TAB
|
||||
{/if}
|
||||
</span>
|
||||
{#if loading}
|
||||
to cancel
|
||||
{:else if generatedContent}
|
||||
to accept
|
||||
{:else}
|
||||
to generate
|
||||
{/if}
|
||||
</div>
|
||||
</span>
|
||||
<span
|
||||
class="text-sm leading-6 indent-[8.5rem] text-gray-500 dark:text-gray-400 pr-1"
|
||||
bind:this={genEl}
|
||||
>
|
||||
{generatedContent}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
<slot {updateFocus} {active} classNames={active && !generatedContent ? '!pl-[8.7rem]' : ''} />
|
||||
</div>
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { ExternalLink, ZapIcon, ZapOffIcon } from 'lucide-svelte'
|
||||
import { ExternalLink, Sparkles, ZapOffIcon } from 'lucide-svelte'
|
||||
import Button from '../common/button/Button.svelte'
|
||||
import { copilotInfo, metadataCompletionEnabled } from '$lib/stores'
|
||||
import Popover from '../Popover.svelte'
|
||||
import { getLocalSetting, storeLocalSetting } from '$lib/utils'
|
||||
import SparklesOffIcon from '../icons/SparklesOffIcon.svelte'
|
||||
|
||||
const SETTING_NAME = 'metadataCompletionEnabled'
|
||||
function loadSetting() {
|
||||
@@ -21,16 +22,21 @@
|
||||
{#if $copilotInfo.exists_openai_resource_path}
|
||||
<Popover>
|
||||
<svelte:fragment slot="text"
|
||||
>{$metadataCompletionEnabled ? 'Disable' : 'Enable'} metadata completion (applies only to you)</svelte:fragment
|
||||
>Metadata completion: {$metadataCompletionEnabled ? 'disabled' : 'enabled'} (applies only to you)</svelte:fragment
|
||||
>
|
||||
<Button
|
||||
size="xs"
|
||||
color="light"
|
||||
startIcon={{
|
||||
icon: $metadataCompletionEnabled ? ZapIcon : ZapOffIcon
|
||||
icon: $metadataCompletionEnabled ? Sparkles : SparklesOffIcon,
|
||||
classes: $metadataCompletionEnabled
|
||||
? 'text-sky-900 dark:text-sky-200'
|
||||
: 'text-sky-900/50 dark:text-sky-200/50'
|
||||
}}
|
||||
on:click={() => {
|
||||
storeSetting()
|
||||
}}
|
||||
btnClasses={$metadataCompletionEnabled ? 'bg-sky-100 dark:bg-gray-700' : ''}
|
||||
/>
|
||||
</Popover>
|
||||
{:else}
|
||||
|
||||
@@ -59,11 +59,13 @@
|
||||
await sleep(300)
|
||||
showDiff()
|
||||
} catch (err) {
|
||||
if (err?.message) {
|
||||
sendUserToast('Failed to generate code: ' + err.message, true)
|
||||
} else {
|
||||
sendUserToast('Failed to generate code', true)
|
||||
console.error(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)
|
||||
}
|
||||
}
|
||||
closePopup()
|
||||
} finally {
|
||||
@@ -165,6 +167,9 @@
|
||||
startIcon={genLoading ? undefined : { icon: Wand2 }}
|
||||
nonCaptureEvent={!genLoading}
|
||||
on:click={genLoading ? () => abortController?.abort() : undefined}
|
||||
btnClasses={genLoading
|
||||
? ''
|
||||
: 'text-sky-900 bg-sky-100 dark:text-sky-200 dark:bg-gray-700 hover:bg-surface-hover dark:hover:bg-surface-hover'}
|
||||
>{#if genLoading}
|
||||
<WindmillIcon
|
||||
white
|
||||
|
||||
@@ -19,9 +19,10 @@
|
||||
import LoadingIcon from '../apps/svelte-select/lib/LoadingIcon.svelte'
|
||||
import { sleep } from '$lib/utils'
|
||||
import { autoPlacement } from '@floating-ui/core'
|
||||
import { Ban, Check, ExternalLink, HistoryIcon, Wand2, X, ZapIcon } from 'lucide-svelte'
|
||||
import { Ban, Bot, Check, ExternalLink, HistoryIcon, Wand2, X } from 'lucide-svelte'
|
||||
import { fade } from 'svelte/transition'
|
||||
import { isInitialCode } from '$lib/script_helpers'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
// props
|
||||
export let iconOnly: boolean = false
|
||||
@@ -97,6 +98,7 @@
|
||||
}
|
||||
} finally {
|
||||
genLoading = false
|
||||
blockPopupOpen = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,22 +272,31 @@
|
||||
color={genLoading ? 'red' : 'light'}
|
||||
btnClasses={genLoading
|
||||
? '!px-3 z-[5000]'
|
||||
: '!px-2 !bg-surface-secondary hover:!bg-surface-hover'}
|
||||
: '!px-2 bg-sky-50 dark:bg-gray-700 dark:hover:bg-surface-hover'}
|
||||
nonCaptureEvent={!genLoading}
|
||||
on:click={genLoading ? () => abortController?.abort() : undefined}
|
||||
bind:element={button}
|
||||
iconOnly
|
||||
title="Generate code from Prompt"
|
||||
startIcon={{ icon: genLoading ? Ban : Wand2 }}
|
||||
startIcon={genLoading
|
||||
? { icon: Ban }
|
||||
: { icon: Wand2, classes: 'text-sky-900 dark:text-sky-200 ' }}
|
||||
/>
|
||||
{:else}
|
||||
<Button
|
||||
title="Generate code from prompt"
|
||||
btnClasses={'!font-medium ' + (genLoading ? 'z-[5000]' : '')}
|
||||
btnClasses={twMerge(
|
||||
'!font-medium',
|
||||
genLoading
|
||||
? 'z-[5000]'
|
||||
: 'text-sky-900 dark:text-sky-200 bg-sky-50 dark:bg-gray-700 dark:hover:bg-surface-hover'
|
||||
)}
|
||||
size="xs"
|
||||
color={genLoading ? 'red' : 'light'}
|
||||
spacingSize="md"
|
||||
startIcon={genLoading ? undefined : { icon: Wand2 }}
|
||||
startIcon={genLoading
|
||||
? undefined
|
||||
: { icon: Wand2, classes: 'text-sky-900 dark:text-sky-200' }}
|
||||
propagateEvent
|
||||
on:click={genLoading
|
||||
? () => abortController?.abort()
|
||||
@@ -299,6 +310,7 @@
|
||||
}
|
||||
}}
|
||||
bind:element={button}
|
||||
{iconOnly}
|
||||
>
|
||||
{#if genLoading}
|
||||
<WindmillIcon
|
||||
@@ -310,7 +322,7 @@
|
||||
/>
|
||||
Stop
|
||||
{:else}
|
||||
AI
|
||||
AI Gen
|
||||
{/if}
|
||||
</Button>
|
||||
{/if}
|
||||
@@ -335,7 +347,7 @@
|
||||
</ToggleButtonGroup>
|
||||
|
||||
<div class="text-[0.6rem] text-secondary opacity-60 flex flex-row items-center gap-0.5">
|
||||
GPT-4 Turbo<ZapIcon size={14} />
|
||||
GPT-4 Turbo<Bot size={14} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex w-96">
|
||||
@@ -356,7 +368,7 @@
|
||||
size="xs"
|
||||
color="blue"
|
||||
buttonType="button"
|
||||
btnClasses="!p-1 !w-[38px] !ml-2"
|
||||
btnClasses="!p-1 !w-[38px] !ml-2 text-sky-900 dark:text-sky-200 bg-sky-100 dark:bg-gray-700 hover:bg-surface-hover dark:hover:bg-surface-hover"
|
||||
title="Generate code from prompt"
|
||||
aria-label="Generate"
|
||||
on:click={() => {
|
||||
|
||||
@@ -170,7 +170,7 @@
|
||||
: undefined}
|
||||
slot="trigger"
|
||||
type="button"
|
||||
class="text-primary bg-surface border mx-0.5 focus:outline-none hover:bg-surface-hover focus:ring-4 focus:ring-gray-200 font-medium rounded-full text-sm w-8 h-8 flex items-center justify-center"
|
||||
class=" bg-sky-100 dark:bg-gray-700 text-sky-900 dark:text-sky-200 dark:hover:bg-surface-hover border mx-0.5 focus:outline-none hover:bg-surface-hover focus:ring-4 focus:ring-gray-200 font-medium rounded-full text-sm w-8 h-8 flex items-center justify-center"
|
||||
>
|
||||
<Wand2 size={16} />
|
||||
</button>
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<script lang="ts">
|
||||
export let size = 24
|
||||
</script>
|
||||
|
||||
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M18.37 2.62999L14 6.99999L12.41 5.40999C12.0353 5.03749 11.5284 4.8284 11 4.8284C10.4716 4.8284 9.96473 5.03749 9.59 5.40999L8 6.99999L17 16L18.59 14.41C18.9625 14.0353 19.1716 13.5284 19.1716 13C19.1716 12.4716 18.9625 11.9647 18.59 11.59L17 9.99999L21.37 5.62999C21.7678 5.23216 21.9913 4.6926 21.9913 4.12999C21.9913 3.56738 21.7678 3.02781 21.37 2.62999C20.9722 2.23216 20.4326 2.00867 19.87 2.00867C19.3074 2.00867 18.7678 2.23216 18.37 2.62999Z"
|
||||
stroke="currentcolor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M9 8C7 11 5 11.5 2 12L10 22C12 21 16 17 16 15"
|
||||
stroke="currentcolor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M14.5 17.5L4.5 15"
|
||||
stroke="currentcolor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M22 22L2 2"
|
||||
stroke="currentcolor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
@@ -0,0 +1,55 @@
|
||||
<script lang="ts">
|
||||
export let size = '24'
|
||||
</script>
|
||||
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
class={$$props.class}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 3L10.088 8.813C9.99015 9.11051 9.82379 9.38088 9.60234 9.60234C9.38088 9.82379 9.11051 9.99015 8.813 10.088L3 12L8.813 13.912C9.11051 14.0099 9.38088 14.1762 9.60234 14.3977C9.82379 14.6191 9.99015 14.8895 10.088 15.187L12 21L13.912 15.187C14.0099 14.8895 14.1762 14.6191 14.3977 14.3977C14.6191 14.1762 14.8895 14.0099 15.187 13.912L21 12L15.187 10.088C14.8895 9.99015 14.6191 9.82379 14.3977 9.60234C14.1762 9.38088 14.0099 9.11051 13.912 8.813L12 3Z"
|
||||
stroke="currentcolor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M5 3V7"
|
||||
stroke="currentcolor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M19 17V21"
|
||||
stroke="currentcolor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M3 5H7"
|
||||
stroke="currentcolor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M17 19H21"
|
||||
stroke="currentcolor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M2 22L22 2"
|
||||
stroke="currentcolor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
Reference in New Issue
Block a user