mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-20 00:02:19 +00:00
fix(chat): address review nits
- Namespace hourglass-flip class + keyframes to ai-chat-hourglass-flip so the :global rule can't pick up unrelated classes elsewhere. - Rename contextPickerRow snippet to selectedContextBadges since it now only renders badges (the picker moved to the controls row). - Restore the @ picker in the message-editing surface via a new showInlinePicker prop on AIChatInput, opted into by AIChatMessage. AIChatDisplay still renders its own picker in the controls row.
This commit is contained in:
@@ -330,7 +330,7 @@
|
||||
class="inline-flex items-center gap-1.5 text-2xs text-accent"
|
||||
aria-label="Waiting for your input"
|
||||
>
|
||||
<Hourglass class="w-3 h-3 hourglass-flip" />
|
||||
<Hourglass class="w-3 h-3 ai-chat-hourglass-flip" />
|
||||
Waiting for your input
|
||||
</span>
|
||||
{:else}
|
||||
@@ -536,11 +536,11 @@
|
||||
/* Hourglass flips every 4s with long rests at each upright position.
|
||||
`:global` because the class is applied to a child component's root
|
||||
(Lucide SVG) and Svelte scoped CSS otherwise wouldn't match it. */
|
||||
:global(.hourglass-flip) {
|
||||
animation: hourglass-flip 4s cubic-bezier(0.65, 0, 0.35, 1) infinite;
|
||||
:global(.ai-chat-hourglass-flip) {
|
||||
animation: ai-chat-hourglass-flip 4s cubic-bezier(0.65, 0, 0.35, 1) infinite;
|
||||
transform-origin: center;
|
||||
}
|
||||
@keyframes hourglass-flip {
|
||||
@keyframes ai-chat-hourglass-flip {
|
||||
0%,
|
||||
35% {
|
||||
transform: rotate(0deg);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import AppAvailableContextList from './AppAvailableContextList.svelte'
|
||||
import AvailableContextList from './AvailableContextList.svelte'
|
||||
import ContextElementBadge from './ContextElementBadge.svelte'
|
||||
import ContextTextarea from './ContextTextarea.svelte'
|
||||
import autosize from '$lib/autosize'
|
||||
@@ -9,6 +10,7 @@
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { tick, untrack, type Snippet } from 'svelte'
|
||||
import Portal from '$lib/components/Portal.svelte'
|
||||
import Popover from '$lib/components/meltComponents/Popover.svelte'
|
||||
import { zIndexes } from '$lib/zIndexes'
|
||||
import { ArrowUp, Square } from 'lucide-svelte'
|
||||
import { Button } from '$lib/components/common'
|
||||
@@ -38,6 +40,12 @@
|
||||
loading?: boolean
|
||||
// Called when the user clicks Stop. Defaults to `aiChatManager.cancel()`.
|
||||
onCancel?: () => void
|
||||
// Render an inline `@` picker button above the textarea. Off by
|
||||
// default because the main chat surface renders its own picker in
|
||||
// `AIChatDisplay`'s controls row; opt-in for the message-editing
|
||||
// surface (`AIChatMessage`) which mounts `AIChatInput` standalone
|
||||
// without surrounding controls.
|
||||
showInlinePicker?: boolean
|
||||
}
|
||||
|
||||
let {
|
||||
@@ -56,7 +64,8 @@
|
||||
bottomRightSnippet,
|
||||
onKeyDown = undefined,
|
||||
loading,
|
||||
onCancel
|
||||
onCancel,
|
||||
showInlinePicker = false
|
||||
}: Props = $props()
|
||||
|
||||
// GLOBAL-mode suggestion pool. We pick one at mount-time so each new
|
||||
@@ -449,9 +458,46 @@
|
||||
/>
|
||||
{/snippet}
|
||||
|
||||
{#snippet contextPickerRow()}
|
||||
{#if selectedContext.length > 0}
|
||||
{#snippet selectedContextBadges()}
|
||||
{#if showInlinePicker || selectedContext.length > 0}
|
||||
<div class="flex flex-row flex-wrap items-center gap-1 mb-1">
|
||||
{#if showInlinePicker}
|
||||
<Popover>
|
||||
{#snippet trigger()}
|
||||
<div
|
||||
class="text-primary text-xs flex flex-row items-center font-normal border px-1 rounded-lg hover:bg-surface-hover bg-surface"
|
||||
title="Add context"
|
||||
>
|
||||
@
|
||||
</div>
|
||||
{/snippet}
|
||||
{#snippet content({ close })}
|
||||
{#if aiChatManager.mode === AIMode.APP}
|
||||
<AppAvailableContextList
|
||||
{availableContext}
|
||||
{selectedContext}
|
||||
onSelect={(element) => {
|
||||
void addContextToSelection(element)
|
||||
close()
|
||||
}}
|
||||
/>
|
||||
{:else}
|
||||
<AvailableContextList
|
||||
{availableContext}
|
||||
{selectedContext}
|
||||
onSelect={(element) => {
|
||||
void addContextToSelection(element)
|
||||
close()
|
||||
}}
|
||||
onSelectWorkspaceItem={(element) => {
|
||||
void addContextToSelection(element)
|
||||
close()
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
{/snippet}
|
||||
</Popover>
|
||||
{/if}
|
||||
{#each selectedContext as element (element.type + '-' + element.title)}
|
||||
<ContextElementBadge
|
||||
contextElement={element}
|
||||
@@ -480,7 +526,7 @@
|
||||
>
|
||||
{#if isContextEnabledMode}
|
||||
{#if showContext}
|
||||
{@render contextPickerRow()}
|
||||
{@render selectedContextBadges()}
|
||||
{/if}
|
||||
<div class="relative">
|
||||
<ContextTextarea
|
||||
@@ -508,7 +554,7 @@
|
||||
</div>
|
||||
{:else if aiChatManager.mode === AIMode.APP}
|
||||
{#if showContext}
|
||||
{@render contextPickerRow()}
|
||||
{@render selectedContextBadges()}
|
||||
{/if}
|
||||
<div class={twMerge('relative w-full scroll-pb-2', className)}>
|
||||
<textarea
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
bind:selectedContext
|
||||
initialInstructions={message.content}
|
||||
{editingMessageIndex}
|
||||
showInlinePicker
|
||||
onClickOutside={() => (editingMessageIndex = null)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Escape') {
|
||||
|
||||
Reference in New Issue
Block a user