add ask in search bar + right top icon on homepage + suggestions

This commit is contained in:
centdix
2025-05-29 11:58:03 +02:00
parent a2a1df16f0
commit ffc6cda97f
6 changed files with 115 additions and 28 deletions

View File

@@ -0,0 +1,29 @@
<script lang="ts">
import { Button } from '$lib/components/common'
import { WandSparkles } from 'lucide-svelte'
import { globalChatOpen, globalChatInitialInput } from '$lib/stores'
interface Props {
label?: string
initialInput?: string
onClick?: () => void
}
const { label, initialInput, onClick: onClickProp }: Props = $props()
const onClick = () => {
globalChatOpen.set(true)
if (initialInput) {
globalChatInitialInput.set(initialInput)
}
onClickProp?.()
}
</script>
<Button
size="xs2"
color="blue"
buttonType="button"
on:click={onClick}
startIcon={{ icon: WandSparkles }}>{label}</Button
>

View File

@@ -4,6 +4,7 @@
import { Send, Loader2 } from 'lucide-svelte'
import { chatRequest, prepareSystemMessage } from './core'
import type { ChatCompletionMessageParam } from 'openai/resources/index.mjs'
import { globalChatInitialInput } from '$lib/stores'
let chatHistory = [
{
@@ -17,6 +18,17 @@
let currentReply = $state('')
let messages = $state(chatHistory)
// Suggested questions for the user
const suggestions = $state([
'How do I create a new workflow?',
"What's the difference between scripts and flows?",
'How can I connect to a database?',
'How do I schedule a recurring job?'
])
// Check if there are any user messages
const hasUserMessages = $derived(messages.some((msg) => msg.role === 'user'))
let abortController = new AbortController()
async function handleSubmit() {
@@ -45,6 +57,19 @@
inputValue = ''
isSubmitting = false
}
function submitSuggestion(suggestion: string) {
inputValue = suggestion
handleSubmit()
}
$effect(() => {
if (globalChatInitialInput) {
inputValue = $globalChatInitialInput
globalChatInitialInput.set('')
handleSubmit()
}
})
</script>
<div class="flex flex-col h-full bg-surface z-10">
@@ -75,6 +100,24 @@
</div>
</div>
{/if}
<!-- Suggestion buttons when no user messages yet -->
{#if !hasUserMessages && !isSubmitting}
<div class="w-full pt-4">
<div class="flex flex-wrap gap-2">
{#each suggestions as suggestion}
<Button
on:click={() => submitSuggestion(suggestion)}
size="xs2"
color="gray"
buttonType="button"
>
{suggestion}
</Button>
{/each}
</div>
</div>
{/if}
</div>
<!-- Input Area -->

View File

@@ -9,9 +9,7 @@
type ListableApp,
type ListableRawApp,
type Script,
type SearchJobsIndexResponse
} from '$lib/gen'
import { clickOutside, isMac } from '$lib/utils'
import {
@@ -44,6 +42,7 @@
import Logs from 'lucide-svelte/icons/logs'
import { AwsIcon, GoogleCloudIcon, KafkaIcon, MqttIcon, NatsIcon } from '../icons'
import RunsSearch from './RunsSearch.svelte'
import AskAiButton from '../AskAiButton.svelte'
let open: boolean = false
@@ -264,7 +263,6 @@
return r
}
let queryParseErrors: string[] = []
async function handleSearch() {
@@ -320,8 +318,8 @@
)
}
if (tab === 'runs') {
await tick()
runsSearch?.handleRunSearch(removePrefix(searchTerm, RUNS_PREFIX))
await tick()
runsSearch?.handleRunSearch(removePrefix(searchTerm, RUNS_PREFIX))
}
selectedItem = selectItem(0)
}
@@ -413,7 +411,7 @@
open = false
goto(path)
} else {
window.open(path, "_blank")
window.open(path, '_blank')
}
}
@@ -581,8 +579,7 @@
let runsSearch: RunsSearch
let runSearchRemainingCount: number | undefined = undefined
let runSearchTotalCount: number | undefined = undefined
let indexMetadata: SearchJobsIndexResponse["index_metadata"] = undefined
let indexMetadata: SearchJobsIndexResponse['index_metadata'] = undefined
</script>
{#if open}
@@ -618,6 +615,15 @@
>{placeholderFromPrefix(searchTerm)}</label
>
</div>
{#if (itemMap[tab] ?? []).length === 0 && searchTerm.length > 0}
<AskAiButton
label="Ask AI"
initialInput={searchTerm}
onClick={() => {
closeModal()
}}
/>
{/if}
{#if queryParseErrors.length > 0}
<Popover notClickable placement="bottom-start">
<AlertTriangle size={16} class="text-yellow-500" />
@@ -682,7 +688,9 @@
{#if (itemMap[tab] ?? []).length === 0}
<div class="flex w-full justify-center items-center">
<div class="text-tertiary text-center">
<div class="text-2xl font-bold">Nothing found</div>
<div class="text-2xl font-bold"
>Nothing found, ask the AI to find what you need !</div
>
<div class="text-sm">Tip: press `esc` to quickly clear the search bar</div>
</div>
</div>
@@ -718,20 +726,20 @@
{/if}
</div>
{:else if tab === 'runs'}
<RunsSearch
bind:queryParseErrors
bind:this={runsSearch}
bind:selectedItem
bind:selectedWorkspace
bind:mouseMoved
bind:loadedRuns={itemMap['runs']}
bind:open
{selectItem}
searchTerm={removePrefix(searchTerm, RUNS_PREFIX)}
bind:runSearchRemainingCount
bind:runSearchTotalCount
bind:indexMetadata
/>
<RunsSearch
bind:queryParseErrors
bind:this={runsSearch}
bind:selectedItem
bind:selectedWorkspace
bind:mouseMoved
bind:loadedRuns={itemMap['runs']}
bind:open
{selectItem}
searchTerm={removePrefix(searchTerm, RUNS_PREFIX)}
bind:runSearchRemainingCount
bind:runSearchTotalCount
bind:indexMetadata
/>
{/if}
</div>
</div>

View File

@@ -174,6 +174,7 @@ export const triggerablesByAI = writable<
Record<string, { description: string; onTrigger: ((id: string) => void) | undefined }>
>({})
export const globalChatOpen = writable<boolean>(false)
export const globalChatInitialInput = writable<string>('')
type SQLBaseSchema = {
[schemaKey: string]: {

View File

@@ -460,7 +460,7 @@
on:click={() => openGlobalChat()}
isCollapsed={false}
icon={MessageCircle}
label="Global Chat"
label="Ask AI"
class="!text-xs"
/>
</div>
@@ -527,7 +527,7 @@
on:click={() => openGlobalChat()}
{isCollapsed}
icon={MessageCircle}
label="Global Chat"
label="Ask AI"
class="!text-xs"
/>
</div>
@@ -634,7 +634,7 @@
on:click={() => openGlobalChat()}
{isCollapsed}
icon={MessageCircle}
label="Global Chat"
label="Ask AI"
class="!text-xs"
/>
</div>

View File

@@ -1,6 +1,6 @@
<script lang="ts">
import { AppService, FlowService, type OpenFlow, type Script } from '$lib/gen'
import { userStore, workspaceStore } from '$lib/stores'
import { globalChatOpen, userStore, workspaceStore } from '$lib/stores'
import { Alert, Button, Drawer, DrawerContent, Tab, Tabs } from '$lib/components/common'
import PageHeader from '$lib/components/PageHeader.svelte'
import CreateActionsFlow from '$lib/components/flows/CreateActionsFlow.svelte'
@@ -23,6 +23,7 @@
import { setQuery } from '$lib/navigation'
import { page } from '$app/stores'
import { goto, replaceState } from '$app/navigation'
import AskAiButton from '$lib/components/AskAiButton.svelte'
type Tab = 'hub' | 'workspace'
@@ -214,7 +215,12 @@
</Drawer>
<div>
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8 h-fit-content">
{#if !$globalChatOpen}
<div class="fixed top-4 right-4 z-50">
<AskAiButton />
</div>
{/if}
<div class="max-w-7xl mx-auto px-4 sm:px-8 md:px-8 h-fit-content">
{#if $workspaceStore == 'admins'}
<div class="my-4"></div>